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$)
55   * @version $Revision$ $Date$
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                         tempBeanInfo  = _createCompositeComponentMetadata(compositeBaseParent);
147                         compositeBaseParent.getAttributes().put(
148                                 UIComponent.BEANINFO_KEY, tempBeanInfo);
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 : tempBeanInfo.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                             tempBeanInfo.getBeanDescriptor().
181                                     setValue(UIComponent.ATTRS_WITH_DECLARED_DEFAULT_VALUES, declaredDefaultValues);
182                         }
183                         finally
184                         {
185                             mctx.popCompositeComponentToStack();
186                             
187                             _cachedBeanInfo = tempBeanInfo;
188                         }
189                     }
190                     else
191                     {
192                         // Put the cached instance, but in that case it is not necessary to call
193                         // nextHandler
194                         compositeBaseParent.getAttributes().put(
195                                 UIComponent.BEANINFO_KEY, _cachedBeanInfo);
196                     }
197                 }
198                 else
199                 {
200                     tempBeanInfo = _createCompositeComponentMetadata(compositeBaseParent);
201                     compositeBaseParent.getAttributes().put(
202                             UIComponent.BEANINFO_KEY, tempBeanInfo);
203                     
204                     try
205                     {
206                         mctx.pushCompositeComponentToStack(compositeBaseParent);
207                         
208                         // Store the ccLevel key here
209                         if (!compositeBaseParent.getAttributes().containsKey(CompositeComponentELUtils.LEVEL_KEY))
210                         {
211                             compositeBaseParent.getAttributes()
212                                 .put(CompositeComponentELUtils.LEVEL_KEY, mctx.getCompositeComponentLevel());
213                         }
214                     
215                         _nextHandler.apply(ctx, parent);
216                         
217                         Collection<String> declaredDefaultValues = null;
218                         
219                         for (PropertyDescriptor pd : tempBeanInfo.getPropertyDescriptors())
220                         {
221                             if (pd.getValue("default") != null)
222                             {
223                                 if (declaredDefaultValues  == null)
224                                 {
225                                     declaredDefaultValues = new ArrayList<String>();
226                                 }
227                                 declaredDefaultValues.add(pd.getName());
228                             }
229                         }
230                         if (declaredDefaultValues == null)
231                         {
232                             declaredDefaultValues = Collections.emptyList();
233                         }
234                         tempBeanInfo.getBeanDescriptor().
235                                 setValue(UIComponent.ATTRS_WITH_DECLARED_DEFAULT_VALUES, declaredDefaultValues);
236                     }
237                     finally
238                     {
239                         mctx.popCompositeComponentToStack();
240                     }
241                 }
242             }
243         }
244         else
245         {
246             try
247             {
248                 mctx.pushCompositeComponentToStack(compositeBaseParent);
249 
250                 // Store the ccLevel key here
251                 if (!compositeBaseParent.getAttributes().containsKey(CompositeComponentELUtils.LEVEL_KEY))
252                 {
253                     compositeBaseParent.getAttributes()
254                         .put(CompositeComponentELUtils.LEVEL_KEY, mctx.getCompositeComponentLevel());
255                 }
256             
257                 _nextHandler.apply(ctx, parent);
258             }
259             finally
260             {
261                 mctx.popCompositeComponentToStack();
262             }
263         }
264     }
265     
266     private CompositeComponentBeanInfo _createCompositeComponentMetadata(UIComponent parent)
267     {
268         BeanDescriptor descriptor = new BeanDescriptor(parent.getClass());
269         CompositeComponentBeanInfo beanInfo = new CompositeComponentBeanInfo(descriptor);
270         return beanInfo;
271     }
272 }