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