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 org.apache.myfaces.view.facelets.tag.composite;
20  
21  import java.beans.BeanDescriptor;
22  import java.beans.IntrospectionException;
23  import java.beans.PropertyDescriptor;
24  import java.io.IOException;
25  import java.util.HashMap;
26  import java.util.Map;
27  import java.util.logging.Level;
28  import java.util.logging.Logger;
29  
30  import javax.faces.application.ProjectStage;
31  import javax.faces.component.UIComponent;
32  import javax.faces.context.FacesContext;
33  import javax.faces.view.facelets.FaceletContext;
34  import javax.faces.view.facelets.TagAttribute;
35  import javax.faces.view.facelets.TagConfig;
36  import javax.faces.view.facelets.TagException;
37  import javax.faces.view.facelets.TagHandler;
38  
39  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletAttribute;
40  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFaceletTag;
41  
42  /**
43   * Define the facets used by this composite component.
44   * <p>
45   * This tag is used inside composite:interface tag. All facets
46   * should be saved under the key UIComponent.FACETS_KEY on the
47   * bean descriptor map as a Map&lt;String, PropertyDescriptor&gt;
48   * </p>
49   * 
50   * @author Leonardo Uribe (latest modification by $Author$)
51   * @version $Revision$ $Date$
52   */
53  @JSFFaceletTag(name="composite:facet")
54  public class FacetHandler extends TagHandler implements InterfaceDescriptorCreator
55  {
56  
57      private static final Logger log = Logger.getLogger(FacetHandler.class.getName());
58      
59      /**
60       * String array defining all standard attributes of this tag.
61       * ATTENTION: this array MUST be sorted alphabetically in order to use binary search!!
62       */
63      private static final String[] STANDARD_ATTRIBUTES_SORTED = new String[]
64      {
65          "displayName",
66          "expert",
67          "hidden",
68          "name",
69          "preferred",
70          "required",
71          "shortDescription"
72      };
73  
74      /**
75       * 
76       */
77      @JSFFaceletAttribute(name="name",
78              className="javax.el.ValueExpression",
79              deferredValueType="java.lang.String")
80      private final TagAttribute _name;
81  
82      /**
83       * Only available if ProjectStage is Development.
84       */
85      @JSFFaceletAttribute(name="displayName",
86              className="javax.el.ValueExpression",
87              deferredValueType="java.lang.String")
88      private final TagAttribute _displayName;
89  
90      /**
91       * Indicate if the attribute is required or not
92       * <p>
93       * Myfaces specific feature: this attribute is checked only if project stage is
94       * not ProjectStage.Production when a composite component is created.
95       * </p>
96       */
97      @JSFFaceletAttribute(name="required",
98              className="javax.el.ValueExpression",
99              deferredValueType="boolean")
100     private final TagAttribute _required;
101 
102     /**
103      * Only available if ProjectStage is Development.
104      */
105     @JSFFaceletAttribute(name="preferred",
106             className="javax.el.ValueExpression",
107             deferredValueType="boolean")
108     private final TagAttribute _preferred;
109 
110     /**
111      * Only available if ProjectStage is Development.
112      */
113     @JSFFaceletAttribute(name="expert",
114             className="javax.el.ValueExpression",
115             deferredValueType="boolean")
116     private final TagAttribute _expert;
117 
118     /**
119      * Only available if ProjectStage is Development.
120      */
121     @JSFFaceletAttribute(name="shortDescription",
122             className="javax.el.ValueExpression",
123             deferredValueType="java.lang.String")
124     private final TagAttribute _shortDescription;
125     
126     /**
127      * The "hidden" flag is used to identify features that are intended only 
128      * for tool use, and which should not be exposed to humans.
129      * Only available if ProjectStage is Development.
130      */
131     @JSFFaceletAttribute(name="hidden",
132             className="javax.el.ValueExpression",
133             deferredValueType="boolean")
134     protected final TagAttribute _hidden;
135     
136     /**
137      * Check if the PropertyDescriptor instance created by this handler
138      * can be cacheable or not. 
139      */
140     private boolean _cacheable;
141     
142     /**
143      * Cached instance used by this component. Note here we have a 
144      * "racy single-check". If this field is used, it is supposed 
145      * the object cached by this handler is immutable, and this is
146      * granted if all properties not saved as ValueExpression are
147      * "literal". 
148      */
149     private PropertyDescriptor _propertyDescriptor; 
150     
151     public FacetHandler(TagConfig config)
152     {
153         super(config);
154         _name = getRequiredAttribute("name");
155         _displayName = getAttribute("displayName");
156         _required = getAttribute("required");
157         _preferred = getAttribute("preferred");
158         _expert = getAttribute("expert");
159         _shortDescription = getAttribute("shortDescription");
160         _hidden = getAttribute("hidden");
161         
162         // We can reuse the same PropertyDescriptor only if the properties
163         // that requires to be evaluated when apply (build view time)
164         // occur are literal or null. Otherwise we need to create it.
165         // Note that only if ProjectStage is Development, The "displayName",
166         // "shortDescription", "expert", "hidden", and "preferred" attributes are exposed
167         final boolean development = FacesContext.getCurrentInstance()
168                 .isProjectStage(ProjectStage.Development);
169         
170         if (_name.isLiteral() 
171                 && (!development || _areDevelopmentAttributesLiteral()))
172         {
173             // Unfortunately its not possible to create the required 
174             // PropertyDescriptor instance here, because there is no way 
175             // to get a FaceletContext to create ValueExpressions. It is
176             // possible to create it if we not have set all this properties:
177             // required and possible unspecified attributes. This prevents 
178             // the racy single-check.
179             _cacheable = true;
180             if (_required == null &&
181                     !CompositeTagAttributeUtils.containsUnspecifiedAttributes(tag, 
182                             STANDARD_ATTRIBUTES_SORTED))
183             {
184                 _propertyDescriptor = _createFacetPropertyDescriptor(development);
185             }
186         }
187         else
188         {
189             _cacheable = false;
190         }
191     }
192     
193     /**
194      * True if the "displayName", "shortDescription", "expert", "hidden", and
195      * "preferred" attributes are either null or literal.
196      * @return
197      */
198     private boolean _areDevelopmentAttributesLiteral()
199     {
200         return CompositeTagAttributeUtils.areAttributesLiteral(
201                 _displayName, _shortDescription, _expert, _hidden, _preferred);
202     }
203 
204     @SuppressWarnings("unchecked")
205     public void apply(FaceletContext ctx, UIComponent parent)
206             throws IOException
207     {
208         CompositeComponentBeanInfo beanInfo = 
209             (CompositeComponentBeanInfo) parent.getAttributes()
210             .get(UIComponent.BEANINFO_KEY);
211         
212         if (beanInfo == null)
213         {
214             if (log.isLoggable(Level.SEVERE))
215             {
216                 log.severe("Cannot find composite bean descriptor UIComponent.BEANINFO_KEY ");
217             }
218             return;
219         }
220         
221         BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
222 
223         Map<String, PropertyDescriptor> facetPropertyDescriptorMap = (Map<String, PropertyDescriptor>)
224             beanDescriptor.getValue(UIComponent.FACETS_KEY);
225         
226         if (facetPropertyDescriptorMap == null)
227         {
228             facetPropertyDescriptorMap = new HashMap<String, PropertyDescriptor>();
229             beanDescriptor.setValue(UIComponent.FACETS_KEY, facetPropertyDescriptorMap);
230         }
231         
232         String facetName = _name.getValue(ctx);
233         
234         if (isCacheable())
235         {
236             if (_propertyDescriptor == null)
237             {
238                 _propertyDescriptor = _createFacetPropertyDescriptor(facetName, ctx);
239             }
240             facetPropertyDescriptorMap.put(facetName, _propertyDescriptor);
241         }
242         else
243         {
244             PropertyDescriptor facetDescriptor = _createFacetPropertyDescriptor(facetName, ctx);
245             facetPropertyDescriptorMap.put(facetName, facetDescriptor);
246         }
247                 
248         nextHandler.apply(ctx, parent);
249     }
250     
251     /**
252      * This method could be called only if it is not necessary to set the following properties:
253      * targets, default, required, methodSignature and type
254      * 
255      * @return
256      */
257     private PropertyDescriptor _createFacetPropertyDescriptor(boolean development)
258     {
259         try
260         {
261             CompositeComponentPropertyDescriptor facetPropertyDescriptor = 
262                 new CompositeComponentPropertyDescriptor(_name.getValue());
263             
264             // If ProjectStage is Development, The "displayName", "shortDescription",
265             // "expert", "hidden", and "preferred" attributes are exposed
266             if (development)
267             {
268                 CompositeTagAttributeUtils.addDevelopmentAttributesLiteral(facetPropertyDescriptor,
269                         _displayName, _shortDescription, _expert, _hidden, _preferred);
270             }
271             
272             // note that no unspecified attributes are handled here, because the current
273             // tag does not contain any, otherwise this code would not have been called.
274             
275             return facetPropertyDescriptor;
276         }
277         catch (IntrospectionException e)
278         {
279             if (log.isLoggable(Level.SEVERE))
280             {
281                 log.log(Level.SEVERE, "Cannot create PropertyDescriptor for facet ",e);
282             }
283             throw new TagException(tag,e);
284         }
285     }
286     
287     private PropertyDescriptor _createFacetPropertyDescriptor(String facetName, FaceletContext ctx)
288         throws TagException, IOException
289     {
290         try
291         {
292             CompositeComponentPropertyDescriptor facetPropertyDescriptor = 
293                 new CompositeComponentPropertyDescriptor(facetName);
294             
295             if (_required != null)
296             {
297                 facetPropertyDescriptor.setValue("required", _required.getValueExpression(ctx, Boolean.class));
298             }
299             
300             // If ProjectStage is Development, The "displayName", "shortDescription",
301             // "expert", "hidden", and "preferred" attributes are exposed
302             if (ctx.getFacesContext().isProjectStage(ProjectStage.Development))
303             {
304                 CompositeTagAttributeUtils.addDevelopmentAttributes(facetPropertyDescriptor, ctx, 
305                         _displayName, _shortDescription, _expert, _hidden, _preferred);
306             }
307             
308             // Any additional attributes are exposed as attributes accessible
309             // from the getValue() and attributeNames() methods on FeatureDescriptor
310             CompositeTagAttributeUtils.addUnspecifiedAttributes(facetPropertyDescriptor, tag, 
311                     STANDARD_ATTRIBUTES_SORTED, ctx);
312             
313             return facetPropertyDescriptor;
314         }
315         catch (IntrospectionException e)
316         {
317             if (log.isLoggable(Level.SEVERE))
318             {
319                 log.log(Level.SEVERE, "Cannot create PropertyDescriptor for attribute ",e);
320             }
321             throw new TagException(tag,e);
322         }
323     }
324 
325     public boolean isCacheable()
326     {
327         return _cacheable;
328     }
329     
330     public void setCacheable(boolean cacheable)
331     {
332         _cacheable = cacheable;
333     }
334 
335     //@Override
336     //public FaceletHandler getNextHandler()
337     //{
338     //    return nextHandler;
339     //}
340 }