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.BeanInfo;
23  import java.beans.PropertyDescriptor;
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Collections;
28  
29  import javax.faces.component.UIComponent;
30  import javax.faces.view.facelets.FaceletContext;
31  import javax.faces.view.facelets.FaceletHandler;
32  
33  import org.apache.myfaces.view.facelets.AbstractFaceletContext;
34  import org.apache.myfaces.view.facelets.FaceletCompositionContext;
35  import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
36  import org.apache.myfaces.view.facelets.tag.TagHandlerUtils;
37  
38  /**
39   * This handler wraps a composite component definition. 
40   * <p>
41   * This handler is set by facelets compiler through 
42   * CompositeComponentUnit class by the presence of cc:interface 
43   * or cc:implementation tag.
44   * </p> 
45   * <p>
46   * The presence of this class has the following objectives:
47   * </p>
48   * <ul>
49   * <li>Cache the BeanInfo instance for a composite component</li>
50   * <li>Set a Location object to resolve #{cc} correctly</li>
51   * <li>Push the current composite component on FaceletCompositionContext stack</li>
52   * <li>Set the attributes with declared default values</li>
53   * </ul>
54   * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
55   * @version $Revision: 1351625 $ $Date: 2012-06-19 04:48:54 -0500 (Tue, 19 Jun 2012) $
56   */
57  public final class CompositeComponentDefinitionTagHandler implements FaceletHandler
58  {
59      private final FaceletHandler _nextHandler;
60      
61      private boolean _cacheable;
62      
63      /**
64       * Cached instance used by this component. Note here we have a 
65       * "racy single-check".If this field is used, it is supposed 
66       * the object cached by this handler is immutable, and this is
67       * granted if all properties not saved as ValueExpression are
68       * "literal". 
69       **/
70      private BeanInfo _cachedBeanInfo;
71      
72      private InterfaceHandler _interfaceHandler;
73      
74      private ImplementationHandler _implementationHandler;
75      
76      public CompositeComponentDefinitionTagHandler(FaceletHandler next)
77      {
78          this._nextHandler = next;
79          
80          _cacheable = true;
81          
82          _interfaceHandler = TagHandlerUtils.findFirstNextByType(_nextHandler, InterfaceHandler.class);
83          
84          _implementationHandler = TagHandlerUtils.findFirstNextByType(_nextHandler, ImplementationHandler.class);
85          
86          Collection<InterfaceDescriptorCreator> metadataInterfaceHandlerList = 
87              TagHandlerUtils.findNextByType( _nextHandler, InterfaceDescriptorCreator.class);
88          
89          for (InterfaceDescriptorCreator handler : metadataInterfaceHandlerList)
90          {
91              if (!handler.isCacheable())
92              {
93                  _cacheable = false;
94                  break;
95              }
96          }
97          if (!_cacheable)
98          {
99              for (InterfaceDescriptorCreator handler : metadataInterfaceHandlerList)
100             {
101                 handler.setCacheable(false);
102             }
103         }
104     }
105 
106     public void apply(FaceletContext ctx, UIComponent parent)
107             throws IOException
108     {
109         FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance(ctx);
110         AbstractFaceletContext actx = (AbstractFaceletContext)ctx;
111         UIComponent compositeBaseParent = actx.isBuildingCompositeComponentMetadata() ? parent : parent.getParent();
112         
113         // Store the current Location on the parent (the location is needed
114         // to resolve the related composite component via #{cc} properly).
115         if (_interfaceHandler != null)
116         {
117             if (!compositeBaseParent.getAttributes().containsKey(CompositeComponentELUtils.LOCATION_KEY))
118             {
119                 compositeBaseParent.getAttributes()
120                     .put(CompositeComponentELUtils.LOCATION_KEY, this._interfaceHandler.getLocation());
121             }
122         }
123         else if (_implementationHandler != null)
124         {
125             if (!compositeBaseParent.getAttributes().containsKey(CompositeComponentELUtils.LOCATION_KEY))
126             {
127                 compositeBaseParent.getAttributes()
128                     .put(CompositeComponentELUtils.LOCATION_KEY, this._implementationHandler.getLocation());
129             }
130         }
131         
132         // Only apply if we are building composite component metadata,
133         // in other words we are calling ViewDeclarationLanguage.getComponentMetadata
134         if ( actx.isBuildingCompositeComponentMetadata() )
135         {
136             CompositeComponentBeanInfo tempBeanInfo = 
137                 (CompositeComponentBeanInfo) compositeBaseParent.getAttributes()
138                 .get(UIComponent.BEANINFO_KEY);
139             
140             if (tempBeanInfo == null)
141             {
142                 if (_cacheable)
143                 {
144                     if (_cachedBeanInfo == null)
145                     {
146                         _cachedBeanInfo = _createCompositeComponentMetadata(ctx, compositeBaseParent);
147                         compositeBaseParent.getAttributes().put(
148                                 UIComponent.BEANINFO_KEY, _cachedBeanInfo);
149                         
150                         try
151                         {
152                             mctx.pushCompositeComponentToStack(compositeBaseParent);
153                             
154                             // Store the ccLevel key here
155                             if (!compositeBaseParent.getAttributes().containsKey(CompositeComponentELUtils.LEVEL_KEY))
156                             {
157                                 compositeBaseParent.getAttributes()
158                                     .put(CompositeComponentELUtils.LEVEL_KEY, mctx.getCompositeComponentLevel());
159                             }
160 
161                             _nextHandler.apply(ctx, parent);
162                             
163                             Collection<String> declaredDefaultValues = null;
164                             
165                             for (PropertyDescriptor pd : _cachedBeanInfo.getPropertyDescriptors())
166                             {
167                                 if (pd.getValue("default") != null)
168                                 {
169                                     if (declaredDefaultValues  == null)
170                                     {
171                                         declaredDefaultValues = new ArrayList<String>();
172                                     }
173                                     declaredDefaultValues.add(pd.getName());
174                                 }
175                             }
176                             if (declaredDefaultValues == null)
177                             {
178                                 declaredDefaultValues = Collections.emptyList();
179                             }
180                             _cachedBeanInfo.getBeanDescriptor().
181                                     setValue(UIComponent.ATTRS_WITH_DECLARED_DEFAULT_VALUES, declaredDefaultValues);
182                         }
183                         finally
184                         {
185                             mctx.popCompositeComponentToStack();
186                         }
187                     }
188                     else
189                     {
190                         // Put the cached instance, but in that case it is not necessary to call
191                         // nextHandler
192                         compositeBaseParent.getAttributes().put(
193                                 UIComponent.BEANINFO_KEY, _cachedBeanInfo);
194                     }
195                 }
196                 else
197                 {
198                     tempBeanInfo = _createCompositeComponentMetadata(ctx, compositeBaseParent);
199                     compositeBaseParent.getAttributes().put(
200                             UIComponent.BEANINFO_KEY, tempBeanInfo);
201                     
202                     try
203                     {
204                         mctx.pushCompositeComponentToStack(compositeBaseParent);
205                         
206                         // Store the ccLevel key here
207                         if (!compositeBaseParent.getAttributes().containsKey(CompositeComponentELUtils.LEVEL_KEY))
208                         {
209                             compositeBaseParent.getAttributes()
210                                 .put(CompositeComponentELUtils.LEVEL_KEY, mctx.getCompositeComponentLevel());
211                         }
212                     
213                         _nextHandler.apply(ctx, parent);
214                         
215                         Collection<String> declaredDefaultValues = null;
216                         
217                         for (PropertyDescriptor pd : tempBeanInfo.getPropertyDescriptors())
218                         {
219                             if (pd.getValue("default") != null)
220                             {
221                                 if (declaredDefaultValues  == null)
222                                 {
223                                     declaredDefaultValues = new ArrayList<String>();
224                                 }
225                                 declaredDefaultValues.add(pd.getName());
226                             }
227                         }
228                         if (declaredDefaultValues == null)
229                         {
230                             declaredDefaultValues = Collections.emptyList();
231                         }
232                         tempBeanInfo.getBeanDescriptor().
233                                 setValue(UIComponent.ATTRS_WITH_DECLARED_DEFAULT_VALUES, declaredDefaultValues);
234                     }
235                     finally
236                     {
237                         mctx.popCompositeComponentToStack();
238                     }
239                 }
240             }
241         }
242         else
243         {
244             try
245             {
246                 mctx.pushCompositeComponentToStack(compositeBaseParent);
247 
248                 // Store the ccLevel key here
249                 if (!compositeBaseParent.getAttributes().containsKey(CompositeComponentELUtils.LEVEL_KEY))
250                 {
251                     compositeBaseParent.getAttributes()
252                         .put(CompositeComponentELUtils.LEVEL_KEY, mctx.getCompositeComponentLevel());
253                 }
254             
255                 _nextHandler.apply(ctx, parent);
256             }
257             finally
258             {
259                 mctx.popCompositeComponentToStack();
260             }
261         }
262     }
263     
264     private CompositeComponentBeanInfo _createCompositeComponentMetadata(
265             FaceletContext ctx, UIComponent parent)
266     {
267         BeanDescriptor descriptor = new BeanDescriptor(parent.getClass());
268         CompositeComponentBeanInfo beanInfo = new CompositeComponentBeanInfo(descriptor);
269         return beanInfo;
270     }
271 }