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.io.IOException;
24  import java.util.Collection;
25  
26  import javax.faces.component.UIComponent;
27  import javax.faces.view.facelets.FaceletContext;
28  import javax.faces.view.facelets.FaceletHandler;
29  
30  import org.apache.myfaces.view.facelets.AbstractFaceletContext;
31  import org.apache.myfaces.view.facelets.FaceletCompositionContext;
32  import org.apache.myfaces.view.facelets.el.CompositeComponentELUtils;
33  import org.apache.myfaces.view.facelets.tag.TagHandlerUtils;
34  
35  /**
36   * This handler wraps a composite component definition. 
37   * <p>
38   * This handler is set by facelets compiler through 
39   * CompositeComponentUnit class by the presence of cc:interface 
40   * or cc:implementation tag.
41   * </p> 
42   * <p>
43   * The presence of this class has the following objectives:
44   * </p>
45   * <ul>
46   * <li>Cache the BeanInfo instance for a composite component</li>
47   * <li>Set a Location object to resolve #{cc} correctly</li>
48   * <li>Push the current composite component on FaceletCompositionContext stack</li>
49   * </ul>
50   * @author Leonardo Uribe (latest modification by $Author: bommel $)
51   * @version $Revision: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $
52   */
53  public final class CompositeComponentDefinitionTagHandler implements FaceletHandler
54  {
55      private final FaceletHandler _nextHandler;
56      
57      private boolean _cacheable;
58      
59      /**
60       * Cached instance used by this component. Note here we have a 
61       * "racy single-check".If this field is used, it is supposed 
62       * the object cached by this handler is immutable, and this is
63       * granted if all properties not saved as ValueExpression are
64       * "literal". 
65       **/
66      private BeanInfo _cachedBeanInfo;
67      
68      private InterfaceHandler _interfaceHandler;
69      
70      private ImplementationHandler _implementationHandler;
71      
72      public CompositeComponentDefinitionTagHandler(FaceletHandler next)
73      {
74          this._nextHandler = next;
75          
76          _cacheable = true;
77          
78          _interfaceHandler = TagHandlerUtils.findFirstNextByType(_nextHandler, InterfaceHandler.class);
79          
80          _implementationHandler = TagHandlerUtils.findFirstNextByType(_nextHandler, ImplementationHandler.class);
81          
82          Collection<InterfaceDescriptorCreator> metadataInterfaceHandlerList = 
83              TagHandlerUtils.findNextByType( _nextHandler, InterfaceDescriptorCreator.class);
84          
85          for (InterfaceDescriptorCreator handler : metadataInterfaceHandlerList)
86          {
87              if (!handler.isCacheable())
88              {
89                  _cacheable = false;
90                  break;
91              }
92          }
93          if (!_cacheable)
94          {
95              for (InterfaceDescriptorCreator handler : metadataInterfaceHandlerList)
96              {
97                  handler.setCacheable(false);
98              }
99          }
100     }
101 
102     public void apply(FaceletContext ctx, UIComponent parent)
103             throws IOException
104     {
105         FaceletCompositionContext mctx = FaceletCompositionContext.getCurrentInstance(ctx);
106         AbstractFaceletContext actx = (AbstractFaceletContext)ctx;
107         UIComponent compositeBaseParent = actx.isBuildingCompositeComponentMetadata() ? parent : parent.getParent();
108         
109         // Store the current Location on the parent (the location is needed
110         // to resolve the related composite component via #{cc} properly).
111         if (_interfaceHandler != null)
112         {
113             compositeBaseParent.getAttributes()
114                 .put(CompositeComponentELUtils.LOCATION_KEY, this._interfaceHandler.getLocation());
115         }
116         else if (_implementationHandler != null)
117         {
118             compositeBaseParent.getAttributes()
119                 .put(CompositeComponentELUtils.LOCATION_KEY, this._implementationHandler.getLocation());
120         }
121         
122         // Only apply if we are building composite component metadata,
123         // in other words we are calling ViewDeclarationLanguage.getComponentMetadata
124         if ( actx.isBuildingCompositeComponentMetadata() )
125         {
126             CompositeComponentBeanInfo tempBeanInfo = 
127                 (CompositeComponentBeanInfo) compositeBaseParent.getAttributes()
128                 .get(UIComponent.BEANINFO_KEY);
129             
130             if (tempBeanInfo == null)
131             {
132                 if (_cacheable)
133                 {
134                     if (_cachedBeanInfo == null)
135                     {
136                         _cachedBeanInfo = _createCompositeComponentMetadata(ctx, compositeBaseParent);
137                         compositeBaseParent.getAttributes().put(
138                                 UIComponent.BEANINFO_KEY, _cachedBeanInfo);
139                         
140                         try
141                         {
142                             mctx.pushCompositeComponentToStack(compositeBaseParent);
143 
144                             _nextHandler.apply(ctx, parent);
145                         }
146                         finally
147                         {
148                             mctx.popCompositeComponentToStack();
149                         }
150                     }
151                     else
152                     {
153                         // Put the cached instance, but in that case it is not necessary to call
154                         // nextHandler
155                         compositeBaseParent.getAttributes().put(
156                                 UIComponent.BEANINFO_KEY, _cachedBeanInfo);
157                     }
158                 }
159                 else
160                 {
161                     tempBeanInfo = _createCompositeComponentMetadata(ctx, compositeBaseParent);
162                     compositeBaseParent.getAttributes().put(
163                             UIComponent.BEANINFO_KEY, tempBeanInfo);
164                     
165                     try
166                     {
167                         mctx.pushCompositeComponentToStack(compositeBaseParent);
168                     
169                         _nextHandler.apply(ctx, parent);
170                         
171                     }
172                     finally
173                     {
174                         mctx.popCompositeComponentToStack();
175                     }
176                 }
177             }
178         }
179         else
180         {
181             try
182             {
183                 mctx.pushCompositeComponentToStack(compositeBaseParent);
184             
185                 _nextHandler.apply(ctx, parent);
186             }
187             finally
188             {
189                 mctx.popCompositeComponentToStack();
190             }
191         }
192     }
193     
194     private CompositeComponentBeanInfo _createCompositeComponentMetadata(
195             FaceletContext ctx, UIComponent parent)
196     {
197         BeanDescriptor descriptor = new BeanDescriptor(parent.getClass());
198         CompositeComponentBeanInfo beanInfo = new CompositeComponentBeanInfo(descriptor);
199         return beanInfo;
200     }
201 }