001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020 package org.apache.myfaces.tobago.renderkit; 021 022 import org.apache.commons.logging.Log; 023 import org.apache.commons.logging.LogFactory; 024 import org.apache.myfaces.tobago.component.ComponentUtil; 025 026 import javax.faces.FacesException; 027 import javax.faces.component.UIComponent; 028 import javax.faces.component.UISelectMany; 029 import javax.faces.context.FacesContext; 030 import javax.faces.convert.Converter; 031 import javax.faces.convert.ConverterException; 032 import javax.faces.el.ValueBinding; 033 import java.lang.reflect.Array; 034 import java.util.ArrayList; 035 import java.util.List; 036 import java.util.Arrays; 037 038 public class SelectManyRendererBase extends LayoutableRendererBase { 039 040 private static final Log LOG = LogFactory.getLog(SelectManyRendererBase.class); 041 042 public void decode(FacesContext facesContext, UIComponent component) { 043 if (ComponentUtil.isOutputOnly(component)) { 044 return; 045 } 046 if (component instanceof UISelectMany) { 047 UISelectMany uiSelectMany = (UISelectMany) component; 048 049 String[] newValues = (String[]) 050 facesContext.getExternalContext().getRequestParameterValuesMap().get(uiSelectMany.getClientId(facesContext)); 051 if (LOG.isDebugEnabled()) { 052 LOG.debug("decode: key='" + component.getClientId(facesContext) 053 + "' value='" + Arrays.toString(newValues) + "'"); 054 LOG.debug("size ... '" + (newValues != null ? newValues.length : -1) + "'"); 055 if (newValues != null) { 056 for (String newValue : newValues) { 057 LOG.debug("newValues[i] = '" + newValue + "'"); 058 } 059 } 060 } 061 062 if (newValues == null) { 063 newValues = new String[0]; // because no selection will not submitted by browsers 064 } 065 uiSelectMany.setSubmittedValue(newValues); 066 } 067 } 068 069 // the following is copied from myfaces shared RendererUtils 070 public Object getConvertedValue(FacesContext facesContext, UIComponent component, Object submittedValue) 071 throws ConverterException { 072 073 if (submittedValue == null) { 074 return null; 075 } else { 076 if (!(submittedValue instanceof String[])) { 077 throw new ConverterException("Submitted value of type String[] for component : " 078 + component.getClientId(facesContext) + "expected"); 079 } 080 } 081 return getConvertedUISelectManyValue(facesContext, (UISelectMany) component, (String[]) submittedValue); 082 } 083 084 private Object getConvertedUISelectManyValue(FacesContext facesContext, 085 UISelectMany component, 086 String[] submittedValue) 087 throws ConverterException { 088 // Attention! 089 // This code is duplicated in jsfapi component package. 090 // If you change something here please do the same in the other class! 091 092 if (submittedValue == null) { 093 throw new NullPointerException("submittedValue"); 094 } 095 096 ValueBinding vb = component.getValueBinding("value"); 097 Class valueType = null; 098 Class arrayComponentType = null; 099 if (vb != null) { 100 valueType = vb.getType(facesContext); 101 if (valueType != null && valueType.isArray()) { 102 arrayComponentType = valueType.getComponentType(); 103 } 104 } 105 106 Converter converter = component.getConverter(); 107 if (converter == null) { 108 if (valueType == null) { 109 // No converter, and no idea of expected type 110 // --> return the submitted String array 111 return submittedValue; 112 } 113 114 if (List.class.isAssignableFrom(valueType)) { 115 // expected type is a List 116 // --> according to javadoc of UISelectMany we assume that the element type 117 // is java.lang.String, and copy the String array to a new List 118 return Arrays.asList(submittedValue); 119 } 120 121 if (arrayComponentType == null) { 122 throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array"); 123 } 124 125 if (String.class.equals(arrayComponentType)) { 126 return submittedValue; //No conversion needed for String type 127 } 128 if (Object.class.equals(arrayComponentType)) { 129 return submittedValue; //No conversion for Object class 130 } 131 132 try { 133 converter = facesContext.getApplication().createConverter(arrayComponentType); 134 } catch (FacesException e) { 135 LOG.error("No Converter for type " + arrayComponentType.getName() + " found", e); 136 return submittedValue; 137 } 138 } 139 140 // Now, we have a converter... 141 // We determine the type of the component array after converting one of it's elements 142 if (vb != null && arrayComponentType == null 143 && valueType != null && valueType.isArray()) { 144 if (submittedValue.length > 0) { 145 arrayComponentType = converter.getAsObject(facesContext, component, submittedValue[0]).getClass(); 146 } 147 } 148 149 if (valueType == null) { 150 // ...but have no idea of expected type 151 // --> so let's convert it to an Object array 152 int len = submittedValue.length; 153 Object[] convertedValues = (Object[]) Array.newInstance( 154 arrayComponentType == null ? Object.class : arrayComponentType, len); 155 for (int i = 0; i < len; i++) { 156 convertedValues[i] 157 = converter.getAsObject(facesContext, component, submittedValue[i]); 158 } 159 return convertedValues; 160 } 161 162 if (List.class.isAssignableFrom(valueType)) { 163 // Curious case: According to specs we should assume, that the element type 164 // of this List is java.lang.String. But there is a Converter set for this 165 // component. Because the user must know what he is doing, we will convert the values. 166 int length = submittedValue.length; 167 List<Object> list = new ArrayList<Object>(length); 168 for (int i = 0; i < length; i++) { 169 list.add(converter.getAsObject(facesContext, component, submittedValue[i])); 170 } 171 return list; 172 } 173 174 if (arrayComponentType == null) { 175 throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array"); 176 } 177 178 if (arrayComponentType.isPrimitive()) { 179 // primitive array 180 int len = submittedValue.length; 181 Object convertedValues = Array.newInstance(arrayComponentType, len); 182 for (int i = 0; i < len; i++) { 183 Array.set(convertedValues, i, 184 converter.getAsObject(facesContext, component, submittedValue[i])); 185 } 186 return convertedValues; 187 } else { 188 // Object array 189 int length = submittedValue.length; 190 List<Object> convertedValues = new ArrayList<Object>(length); 191 for (int i = 0; i < length; i++) { 192 convertedValues.add(i, converter.getAsObject(facesContext, component, submittedValue[i])); 193 } 194 return convertedValues.toArray((Object[]) Array.newInstance(arrayComponentType, length)); 195 } 196 } 197 198 }