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