View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.renderkit;
21  
22  import org.apache.myfaces.tobago.internal.util.ArrayUtils;
23  import org.apache.myfaces.tobago.util.ComponentUtils;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  import javax.faces.FacesException;
28  import javax.faces.component.UIComponent;
29  import javax.faces.component.UISelectMany;
30  import javax.faces.context.FacesContext;
31  import javax.faces.convert.Converter;
32  import javax.faces.convert.ConverterException;
33  import javax.faces.el.ValueBinding;
34  import java.lang.reflect.Array;
35  import java.util.ArrayList;
36  import java.util.Arrays;
37  import java.util.List;
38  
39  public class SelectManyRendererBase extends LayoutComponentRendererBase {
40  
41    private static final Logger LOG = LoggerFactory.getLogger(SelectManyRendererBase.class);
42  
43    public void decode(final FacesContext facesContext, final UIComponent component) {
44      if (ComponentUtils.isOutputOnly(component)) {
45        return;
46      }
47      if (component instanceof UISelectMany) {
48        final UISelectMany uiSelectMany = (UISelectMany) component;
49  
50        String[] newValues = (String[])
51            facesContext.getExternalContext().getRequestParameterValuesMap().get(uiSelectMany.getClientId(facesContext));
52        if (LOG.isDebugEnabled()) {
53          LOG.debug("decode: key='" + component.getClientId(facesContext)
54              + "' value='" + Arrays.toString(newValues) + "'");
55          LOG.debug("size ... '" + (newValues != null ? newValues.length : -1) + "'");
56          if (newValues != null) {
57            for (final String newValue : newValues) {
58              LOG.debug("newValues[i] = '" + newValue + "'");
59            }
60          }
61        }
62  
63        if (newValues == null) {
64          newValues = ArrayUtils.EMPTY_STRING_ARRAY; // because no selection will not submitted by browsers
65        }
66        uiSelectMany.setSubmittedValue(newValues);
67      }
68    }
69  
70    // the following is copied from myfaces shared RendererUtils
71    public Object getConvertedValue(
72        final FacesContext facesContext, final UIComponent component, final Object submittedValue)
73        throws ConverterException {
74  
75      if (submittedValue == null) {
76        return null;
77      } else {
78        if (!(submittedValue instanceof String[])) {
79          throw new ConverterException("Submitted value of type String[] for component : "
80              + component.getClientId(facesContext) + "expected");
81        }
82      }
83      return getConvertedUISelectManyValue(facesContext, (UISelectMany) component, (String[]) submittedValue);
84    }
85  
86    private Object getConvertedUISelectManyValue(final FacesContext facesContext,
87        final UISelectMany component,
88        final String[] submittedValue)
89        throws ConverterException {
90      // Attention!
91      // This code is duplicated in jsfapi component package.
92      // If you change something here please do the same in the other class!
93  
94      if (submittedValue == null) {
95        throw new NullPointerException("submittedValue");
96      }
97  
98      final ValueBinding vb = component.getValueBinding("value");
99      Class valueType = null;
100     Class arrayComponentType = null;
101     if (vb != null) {
102       valueType = vb.getType(facesContext);
103       if (valueType != null && valueType.isArray()) {
104         arrayComponentType = valueType.getComponentType();
105       }
106     }
107 
108     Converter converter = component.getConverter();
109     if (converter == null) {
110       if (valueType == null) {
111         // No converter, and no idea of expected type
112         // --> return the submitted String array
113         return submittedValue;
114       }
115 
116       if (List.class.isAssignableFrom(valueType)) {
117         // expected type is a List
118         // --> according to javadoc of UISelectMany we assume that the element type
119         //     is java.lang.String, and copy the String array to a new List
120         return Arrays.asList(submittedValue);
121       }
122 
123       if (arrayComponentType == null) {
124         throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array");
125       }
126 
127       if (String.class.equals(arrayComponentType)) {
128         return submittedValue; //No conversion needed for String type
129       }
130       if (Object.class.equals(arrayComponentType)) {
131         return submittedValue; //No conversion for Object class
132       }
133 
134       try {
135         converter = facesContext.getApplication().createConverter(arrayComponentType);
136       } catch (final FacesException e) {
137         LOG.error("No Converter for type " + arrayComponentType.getName() + " found", e);
138         return submittedValue;
139       }
140     }
141 
142     // Now, we have a converter...
143     // We determine the type of the component array after converting one of it's elements
144     if (vb != null && arrayComponentType == null
145         && valueType != null && valueType.isArray()) {
146       if (submittedValue.length > 0) {
147         arrayComponentType = converter.getAsObject(facesContext, component, submittedValue[0]).getClass();
148       }
149     }
150 
151     if (valueType == null) {
152       // ...but have no idea of expected type
153       // --> so let's convert it to an Object array
154       final int len = submittedValue.length;
155       final Object[] convertedValues = (Object[]) Array.newInstance(
156           arrayComponentType == null ? Object.class : arrayComponentType, len);
157       for (int i = 0; i < len; i++) {
158         convertedValues[i]
159             = converter.getAsObject(facesContext, component, submittedValue[i]);
160       }
161       return convertedValues;
162     }
163 
164     if (List.class.isAssignableFrom(valueType)) {
165       // Curious case: According to specs we should assume, that the element type
166       // of this List is java.lang.String. But there is a Converter set for this
167       // component. Because the user must know what he is doing, we will convert the values.
168       final int length = submittedValue.length;
169       final List<Object> list = new ArrayList<Object>(length);
170       for (int i = 0; i < length; i++) {
171         list.add(converter.getAsObject(facesContext, component, submittedValue[i]));
172       }
173       return list;
174     }
175 
176     if (arrayComponentType == null) {
177       throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array");
178     }
179 
180     if (arrayComponentType.isPrimitive()) {
181       // primitive array
182       final int len = submittedValue.length;
183       final Object convertedValues = Array.newInstance(arrayComponentType, len);
184       for (int i = 0; i < len; i++) {
185         Array.set(convertedValues, i,
186             converter.getAsObject(facesContext, component, submittedValue[i]));
187       }
188       return convertedValues;
189     } else {
190       // Object array
191       final int length = submittedValue.length;
192       final List<Object> convertedValues = new ArrayList<Object>(length);
193       for (int i = 0; i < length; i++) {
194         convertedValues.add(i, converter.getAsObject(facesContext, component, submittedValue[i]));
195       }
196       return convertedValues.toArray((Object[]) Array.newInstance(arrayComponentType, length));
197     }
198   }
199 
200 }