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.UIInput;
30  import javax.faces.component.UISelectMany;
31  import javax.faces.context.FacesContext;
32  import javax.faces.convert.Converter;
33  import javax.faces.convert.ConverterException;
34  import javax.faces.el.ValueBinding;
35  import java.lang.reflect.Array;
36  import java.util.ArrayList;
37  import java.util.Arrays;
38  import java.util.List;
39  
40  public class SelectManyRendererBase extends LayoutComponentRendererBase {
41  
42    private static final Logger LOG = LoggerFactory.getLogger(SelectManyRendererBase.class);
43  
44    public void decode(final FacesContext facesContext, final UIComponent component) {
45      if (ComponentUtils.isOutputOnly(component)) {
46        return;
47      }
48      if (component instanceof UISelectMany) {
49        final UISelectMany uiSelectMany = (UISelectMany) component;
50  
51        String[] newValues = (String[])
52            facesContext.getExternalContext().getRequestParameterValuesMap().get(uiSelectMany.getClientId(facesContext));
53        if (LOG.isDebugEnabled()) {
54          LOG.debug("decode: key='" + component.getClientId(facesContext)
55              + "' value='" + Arrays.toString(newValues) + "'");
56          LOG.debug("size ... '" + (newValues != null ? newValues.length : -1) + "'");
57          if (newValues != null) {
58            for (final String newValue : newValues) {
59              LOG.debug("newValues[i] = '" + newValue + "'");
60            }
61          }
62        }
63  
64        if (newValues == null) {
65          newValues = ArrayUtils.EMPTY_STRING_ARRAY; // because no selection will not submitted by browsers
66        }
67        uiSelectMany.setSubmittedValue(newValues);
68      }
69    }
70  
71    public String[] getSubmittedValues(UIInput input) {
72      return (String[]) input.getSubmittedValue();
73    }
74  
75    // the following is copied from myfaces shared RendererUtils
76    public Object getConvertedValue(
77        final FacesContext facesContext, final UIComponent component, final Object submittedValue)
78        throws ConverterException {
79  
80      if (submittedValue == null) {
81        return null;
82      } else {
83        if (!(submittedValue instanceof String[])) {
84          throw new ConverterException("Submitted value of type String[] for component : "
85              + component.getClientId(facesContext) + "expected");
86        }
87      }
88      return getConvertedUISelectManyValue(facesContext, (UISelectMany) component, (String[]) submittedValue);
89    }
90  
91    private Object getConvertedUISelectManyValue(final FacesContext facesContext,
92        final UISelectMany component,
93        final String[] submittedValue)
94        throws ConverterException {
95      // Attention!
96      // This code is duplicated in jsfapi component package.
97      // If you change something here please do the same in the other class!
98  
99      if (submittedValue == null) {
100       throw new NullPointerException("submittedValue");
101     }
102 
103     final ValueBinding vb = component.getValueBinding("value");
104     Class valueType = null;
105     Class arrayComponentType = null;
106     if (vb != null) {
107       valueType = vb.getType(facesContext);
108       if (valueType != null && valueType.isArray()) {
109         arrayComponentType = valueType.getComponentType();
110       }
111     }
112 
113     Converter converter = component.getConverter();
114     if (converter == null) {
115       if (valueType == null) {
116         // No converter, and no idea of expected type
117         // --> return the submitted String array
118         return submittedValue;
119       }
120 
121       if (List.class.isAssignableFrom(valueType)) {
122         // expected type is a List
123         // --> according to javadoc of UISelectMany we assume that the element type
124         //     is java.lang.String, and copy the String array to a new List
125         return Arrays.asList(submittedValue);
126       }
127 
128       if (arrayComponentType == null) {
129         throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array");
130       }
131 
132       if (String.class.equals(arrayComponentType)) {
133         return submittedValue; //No conversion needed for String type
134       }
135       if (Object.class.equals(arrayComponentType)) {
136         return submittedValue; //No conversion for Object class
137       }
138 
139       try {
140         converter = facesContext.getApplication().createConverter(arrayComponentType);
141       } catch (final FacesException e) {
142         LOG.error("No Converter for type " + arrayComponentType.getName() + " found", e);
143         return submittedValue;
144       }
145     }
146 
147     // Now, we have a converter...
148     // We determine the type of the component array after converting one of it's elements
149     if (vb != null && arrayComponentType == null
150         && valueType != null && valueType.isArray()) {
151       if (submittedValue.length > 0) {
152         arrayComponentType = converter.getAsObject(facesContext, component, submittedValue[0]).getClass();
153       }
154     }
155 
156     if (valueType == null) {
157       // ...but have no idea of expected type
158       // --> so let's convert it to an Object array
159       final int len = submittedValue.length;
160       final Object[] convertedValues = (Object[]) Array.newInstance(
161           arrayComponentType == null ? Object.class : arrayComponentType, len);
162       for (int i = 0; i < len; i++) {
163         convertedValues[i]
164             = converter.getAsObject(facesContext, component, submittedValue[i]);
165       }
166       return convertedValues;
167     }
168 
169     if (List.class.isAssignableFrom(valueType)) {
170       // Curious case: According to specs we should assume, that the element type
171       // of this List is java.lang.String. But there is a Converter set for this
172       // component. Because the user must know what he is doing, we will convert the values.
173       final int length = submittedValue.length;
174       final List<Object> list = new ArrayList<Object>(length);
175       for (int i = 0; i < length; i++) {
176         list.add(converter.getAsObject(facesContext, component, submittedValue[i]));
177       }
178       return list;
179     }
180 
181     if (arrayComponentType == null) {
182       throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array");
183     }
184 
185     if (arrayComponentType.isPrimitive()) {
186       // primitive array
187       final int len = submittedValue.length;
188       final Object convertedValues = Array.newInstance(arrayComponentType, len);
189       for (int i = 0; i < len; i++) {
190         Array.set(convertedValues, i,
191             converter.getAsObject(facesContext, component, submittedValue[i]));
192       }
193       return convertedValues;
194     } else {
195       // Object array
196       final int length = submittedValue.length;
197       final List<Object> convertedValues = new ArrayList<Object>(length);
198       for (int i = 0; i < length; i++) {
199         convertedValues.add(i, converter.getAsObject(facesContext, component, submittedValue[i]));
200       }
201       return convertedValues.toArray((Object[]) Array.newInstance(arrayComponentType, length));
202     }
203   }
204 
205 }