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(FacesContext facesContext, UIComponent component) {
44      if (ComponentUtils.isOutputOnly(component)) {
45        return;
46      }
47      if (component instanceof UISelectMany) {
48        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 (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(FacesContext facesContext, UIComponent component, Object submittedValue)
72        throws ConverterException {
73  
74      if (submittedValue == null) {
75        return null;
76      } else {
77        if (!(submittedValue instanceof String[])) {
78          throw new ConverterException("Submitted value of type String[] for component : "
79              + component.getClientId(facesContext) + "expected");
80        }
81      }
82      return getConvertedUISelectManyValue(facesContext, (UISelectMany) component, (String[]) submittedValue);
83    }
84  
85    private Object getConvertedUISelectManyValue(FacesContext facesContext,
86        UISelectMany component,
87        String[] submittedValue)
88        throws ConverterException {
89      // Attention!
90      // This code is duplicated in jsfapi component package.
91      // If you change something here please do the same in the other class!
92  
93      if (submittedValue == null) {
94        throw new NullPointerException("submittedValue");
95      }
96  
97      ValueBinding vb = component.getValueBinding("value");
98      Class valueType = null;
99      Class arrayComponentType = null;
100     if (vb != null) {
101       valueType = vb.getType(facesContext);
102       if (valueType != null && valueType.isArray()) {
103         arrayComponentType = valueType.getComponentType();
104       }
105     }
106 
107     Converter converter = component.getConverter();
108     if (converter == null) {
109       if (valueType == null) {
110         // No converter, and no idea of expected type
111         // --> return the submitted String array
112         return submittedValue;
113       }
114 
115       if (List.class.isAssignableFrom(valueType)) {
116         // expected type is a List
117         // --> according to javadoc of UISelectMany we assume that the element type
118         //     is java.lang.String, and copy the String array to a new List
119         return Arrays.asList(submittedValue);
120       }
121 
122       if (arrayComponentType == null) {
123         throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array");
124       }
125 
126       if (String.class.equals(arrayComponentType)) {
127         return submittedValue; //No conversion needed for String type
128       }
129       if (Object.class.equals(arrayComponentType)) {
130         return submittedValue; //No conversion for Object class
131       }
132 
133       try {
134         converter = facesContext.getApplication().createConverter(arrayComponentType);
135       } catch (FacesException e) {
136         LOG.error("No Converter for type " + arrayComponentType.getName() + " found", e);
137         return submittedValue;
138       }
139     }
140 
141     // Now, we have a converter...
142     // We determine the type of the component array after converting one of it's elements
143     if (vb != null && arrayComponentType == null
144         && valueType != null && valueType.isArray()) {
145       if (submittedValue.length > 0) {
146         arrayComponentType = converter.getAsObject(facesContext, component, submittedValue[0]).getClass();
147       }
148     }
149 
150     if (valueType == null) {
151       // ...but have no idea of expected type
152       // --> so let's convert it to an Object array
153       int len = submittedValue.length;
154       Object[] convertedValues = (Object[]) Array.newInstance(
155           arrayComponentType == null ? Object.class : arrayComponentType, len);
156       for (int i = 0; i < len; i++) {
157         convertedValues[i]
158             = converter.getAsObject(facesContext, component, submittedValue[i]);
159       }
160       return convertedValues;
161     }
162 
163     if (List.class.isAssignableFrom(valueType)) {
164       // Curious case: According to specs we should assume, that the element type
165       // of this List is java.lang.String. But there is a Converter set for this
166       // component. Because the user must know what he is doing, we will convert the values.
167       int length = submittedValue.length;
168       List<Object> list = new ArrayList<Object>(length);
169       for (int i = 0; i < length; i++) {
170         list.add(converter.getAsObject(facesContext, component, submittedValue[i]));
171       }
172       return list;
173     }
174 
175     if (arrayComponentType == null) {
176       throw new IllegalArgumentException("ValueBinding for UISelectMany must be of type List or Array");
177     }
178 
179     if (arrayComponentType.isPrimitive()) {
180       // primitive array
181       int len = submittedValue.length;
182       Object convertedValues = Array.newInstance(arrayComponentType, len);
183       for (int i = 0; i < len; i++) {
184         Array.set(convertedValues, i,
185             converter.getAsObject(facesContext, component, submittedValue[i]));
186       }
187       return convertedValues;
188     } else {
189       // Object array
190       int length = submittedValue.length;
191       List<Object> convertedValues = new ArrayList<Object>(length);
192       for (int i = 0; i < length; i++) {
193         convertedValues.add(i, converter.getAsObject(facesContext, component, submittedValue[i]));
194       }
195       return convertedValues.toArray((Object[]) Array.newInstance(arrayComponentType, length));
196     }
197   }
198 
199 }