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  package javax.faces.component;
20  
21  import java.io.IOException;
22  import java.util.concurrent.ConcurrentHashMap;
23  import java.util.logging.Level;
24  import java.util.logging.Logger;
25  
26  import javax.el.ValueExpression;
27  import javax.faces.FactoryFinder;
28  import javax.faces.application.FacesMessage;
29  import javax.faces.context.FacesContext;
30  import javax.faces.convert.Converter;
31  import javax.faces.convert.ConverterException;
32  import javax.faces.render.RenderKit;
33  import javax.faces.render.RenderKitFactory;
34  import javax.faces.render.Renderer;
35  
36  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
37  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
38  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
39  
40  /**
41   * 
42   * TODO: documentation on jsp and pld are not the same. It appear two
43   * params: maxlength and for, but no property getter and setter founded here. 
44   * If maxlength is used, we can put something like this: 
45   * JSFJspProperty(name = "maxlength", returnType = "java.lang.String")
46   * 
47   * @since 2.0
48   */
49  @JSFComponent(name = "f:viewParam", bodyContent = "JSP", 
50          tagClass = "org.apache.myfaces.taglib.core.ViewParamTag")
51  @JSFJspProperty(name = "maxlength", returnType = "int",
52                  longDesc = "The max number or characters allowed for this param")
53  public class UIViewParameter extends UIInput
54  {
55      private static final Logger log = Logger.getLogger(UIViewParameter.class.getName());
56      public static final String COMPONENT_FAMILY = "javax.faces.ViewParameter";
57      public static final String COMPONENT_TYPE = "javax.faces.ViewParameter";
58  
59      private static final String DELEGATE_FAMILY = UIInput.COMPONENT_FAMILY;
60      private static final String DELEGATE_RENDERER_TYPE = "javax.faces.Text";
61      
62      private static ConcurrentHashMap<ClassLoader,Renderer> delegateRendererMap = 
63          new ConcurrentHashMap<ClassLoader,Renderer>();
64  
65      public UIViewParameter()
66      {
67          setRendererType(null);
68      }
69  
70      @Override
71      public String getFamily()
72      {
73          return COMPONENT_FAMILY;
74      }
75  
76      @Override
77      public void decode(FacesContext context)
78      {
79          // Override behavior from superclass to pull a value from the incoming request parameter map under the 
80          // name given by getName() and store it with a call to UIInput.setSubmittedValue(java.lang.Object).
81          String value = context.getExternalContext().getRequestParameterMap().get(getName());
82          
83          // only apply the value if it is non-null (otherwise postbacks 
84          // to a view with view parameters would not work correctly)
85          if (value != null)
86          {
87              setSubmittedValue(value);
88          }
89      }
90  
91      @Override
92      public void encodeAll(FacesContext context) throws IOException
93      {
94          if (context == null) 
95          {
96              throw new NullPointerException();
97          }
98          setSubmittedValue(getStringValue(context));
99      }
100 
101     public String getName()
102     {
103         return (String) getStateHelper().eval(PropertyKeys.name);
104     }
105 
106     public String getStringValue(FacesContext context)
107     {
108         if (getValueExpression ("value") != null) 
109         {
110             // Value specified as an expression, so do the conversion.
111             
112             return getStringValueFromModel (context);
113         }
114         
115         // Otherwise, just return the local value.
116         
117         return ((String) this.getLocalValue());
118     }
119 
120     public String getStringValueFromModel(FacesContext context) throws ConverterException
121     {
122         ValueExpression ve = getValueExpression ("value");
123         Converter converter;
124         Object value;
125         
126         if (ve == null) 
127         {
128             // No value expression, return null.
129             return null;
130         }
131         
132         value = ve.getValue (context.getELContext());
133         
134         if (value instanceof String) 
135         {
136             // No need to convert.
137             return ((String) value);
138         }
139         
140         converter = getConverter();
141         
142         if (converter == null) 
143         {
144             if (value == null) 
145             {
146                 // No converter, no value, return null.
147                 return null;
148             }
149             
150             // See if we can create the converter from the value type.
151             
152             converter = context.getApplication().createConverter (value.getClass());
153             
154             if (converter == null) 
155             {
156                 // Only option is to call toString().
157                 
158                 return value.toString();
159             }
160         }
161         
162         return converter.getAsString (context, this, value);
163     }
164 
165     @JSFProperty(tagExcluded=true)
166     @Override
167     public boolean isImmediate()
168     {
169         return false;
170     }
171     
172     @JSFProperty(tagExcluded=true)
173     @Override
174     public boolean isRendered()
175     {
176         return super.isRendered();
177     }
178     
179     @Override
180     public void processValidators(FacesContext context)
181     {
182         if (context == null) 
183         {
184             throw new NullPointerException ("context");
185         }
186         
187         // If value is null and required is set, validation fails.
188         
189         if ((getSubmittedValue() == null) && isRequired()) 
190         {
191             FacesMessage message;
192             String required = getRequiredMessage();
193             
194             if (required != null) 
195             {
196                 message = new FacesMessage (FacesMessage.SEVERITY_ERROR, required, required);
197             }
198             else 
199             {
200                 Object label = _MessageUtils.getLabel (context, this);
201                 
202                 message = _MessageUtils.getMessage (context, context.getViewRoot().getLocale(),
203                      FacesMessage.SEVERITY_ERROR, REQUIRED_MESSAGE_ID, new Object[] { label });
204             }
205             
206             setValid (false);
207             
208             context.addMessage (getClientId (context), message);
209             context.validationFailed();
210             context.renderResponse();
211             
212             return;
213         }
214         
215         super.processValidators (context);
216     }
217     
218     enum PropertyKeys
219     {
220         name
221     }
222     
223     public void setName(String name)
224     {
225         getStateHelper().put(PropertyKeys.name, name );
226     }
227 
228     @Override
229     public void updateModel(FacesContext context)
230     {
231         super.updateModel(context);
232         
233         // Put name in request map if value is not a value expression, is valid, and local
234         // value was set.
235         
236         if ((getValueExpression ("value") == null) && isValid() && isLocalValueSet()) 
237         {
238             context.getExternalContext().getRequestMap().put (getName(), getLocalValue());
239         }
240     }
241 
242     @Override
243     protected Object getConvertedValue(FacesContext context, Object submittedValue) throws ConverterException
244     {
245         return getDelegateRenderer(context).getConvertedValue(context, this, submittedValue);
246     }
247 
248     private static Renderer getDelegateRenderer(FacesContext context)
249     {
250         ClassLoader classLoader = _ClassUtils.getContextClassLoader();
251         Renderer delegateRenderer = delegateRendererMap.get(classLoader);
252         if(delegateRenderer == null)
253         {
254             RenderKitFactory factory = (RenderKitFactory) FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
255             RenderKit kit = factory.getRenderKit(context, RenderKitFactory.HTML_BASIC_RENDER_KIT);
256 
257             delegateRenderer = kit.getRenderer(DELEGATE_FAMILY, DELEGATE_RENDERER_TYPE);
258             delegateRendererMap.put(classLoader, delegateRenderer);
259         }
260 
261         return delegateRenderer;
262     }
263 
264     /**
265      * The idea of this method is to be called from AbstractFacesInitializer.
266      */
267     @SuppressWarnings("unused")
268     private static void releaseRenderer() 
269     {
270         if (log.isLoggable(Level.FINEST))
271         {
272             log.finest("releaseRenderer rendererMap -> " + delegateRendererMap.toString());
273         }
274         
275         
276         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
277         
278         if (log.isLoggable(Level.FINEST))
279         {
280             log.finest("releaseRenderer classLoader -> " + classLoader.toString() );
281             log.finest("releaseRenderer renderer -> " + delegateRendererMap.get(classLoader));
282         }
283         
284         
285         delegateRendererMap.remove(classLoader);
286         
287         if (log.isLoggable(Level.FINEST))
288         {
289             log.finest("releaseRenderer renderMap -> " + delegateRendererMap.toString());
290         }
291         
292     }
293 
294     @Override
295     protected FacesContext getFacesContext()
296     {
297         //In theory the parent most of the times has 
298         //the cached FacesContext instance, because this
299         //element is purely logical, and the parent is the one
300         //where encodeXXX was invoked. But only limit the
301         //search to the closest parent.
302         UIComponent parent = getParent();
303         if (parent != null && parent.isCachedFacesContext())
304         {
305             return parent.getFacesContext();
306         }
307         else
308         {
309             return super.getFacesContext();
310         }
311     }
312 
313     /**
314      * @since 2.0
315      */
316     public static class Reference
317     {
318         private int _index;
319         private UIViewParameter _param;
320         private Object _state;
321         private String _viewId;
322 
323         public Reference(FacesContext context, UIViewParameter param, int indexInParent,
324                          String viewIdAtTimeOfConstruction)
325         {
326             // This constructor cause the StateHolder.saveState(javax.faces.context.FacesContext) method
327             // to be called on argument UIViewParameter.
328             _param = param;
329             _viewId = viewIdAtTimeOfConstruction;
330             _index = indexInParent;
331             _state = param.saveState(context);
332         }
333 
334         public UIViewParameter getUIViewParameter(FacesContext context)
335         {
336             // If the current viewId is the same as the viewId passed to our constructor
337             if (context.getViewRoot().getViewId().equals(_viewId))
338             {
339                 // use the index passed to the constructor to find the actual UIViewParameter instance and return it.
340                 // FIXME: How safe is that when dealing with component trees altered by applications?
341                 return (UIViewParameter) _param.getParent().getChildren().get(_index);
342             }
343             else
344             {
345                 // Otherwise, call StateHolder.restoreState(javax.faces.context.FacesContext, java.lang.Object) on
346                 // the saved state and return the result.
347                 _param.restoreState(context, _state);
348 
349                 return _param;
350             }
351         }
352     }
353 }