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