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.el;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.regex.Pattern;
24  
25  import javax.faces.component.UIComponent;
26  import javax.faces.context.FacesContext;
27  import javax.faces.view.Location;
28  
29  /**
30   * Utility class for composite components when used in EL Expressions --> #{cc}
31   * 
32   * @author Jakob Korherr (latest modification by $Author: lu4242 $)
33   * @version $Revision: 1402825 $ $Date: 2012-10-27 12:09:22 -0500 (Sat, 27 Oct 2012) $
34   */
35  public final class CompositeComponentELUtils
36  {
37      
38      /**
39       * The key under which the component stack is stored in the FacesContext.
40       * ATTENTION: this constant is duplicate in UIComponent.
41       */
42      //public static final String COMPONENT_STACK = "componentStack:" + UIComponent.class.getName();
43      
44      /**
45       * The key under which the current composite component is stored in the attribute
46       * map of the FacesContext.
47       */
48      public static final String CURRENT_COMPOSITE_COMPONENT_KEY = "org.apache.myfaces.compositecomponent.current";
49      
50      /**
51       * The key under which the Location of the composite componente is stored
52       * in the attributes map of the component by InterfaceHandler.
53       */
54      public static final String LOCATION_KEY = "org.apache.myfaces.compositecomponent.location";
55  
56      /**
57       * Indicates the nesting level where the composite component was created, working as reference
58       * point to all EL expressions created in that point from Facelets engine.
59       */
60      public static final String LEVEL_KEY = "oam.cc.ccLevel";
61      
62      /**
63       * A regular expression used to determine if cc is used in an expression String.
64       */
65      public static final Pattern CC_EXPRESSION_REGEX = Pattern.compile(".*[^\\w\\.]cc[^\\w].*");
66      
67      /**
68       * A regular expression used to determine if cc.attrs is used as a method expression
69       * in an expression String. This means cc.attrs must occur, must stand before a '(',
70       * because otherwise it would be a method parameter (EL 2.2), and there must be no '.' after
71       * cc.attrs unless there is a left parenthesis before it (e.g. #{cc.attrs.method(bean.parameter)}).
72       * 
73       * Explanation of the parts:
74       * - [^\\(]* - There can be any character except a '(' before cc.attrs
75       * - [^\\w\\.\\(] - There must be no word character, dot, or left parenthesis directly before cc.attrs
76       * - cc\\.attrs\\. - "cc.attrs." must occur
77       * - [^\\.]* - There must be no dot after cc.attrs to indicate a method invocation on cc.attrs
78       * - (\\(.*)? - If there is a left paranthesis after cc.attrs, a dot is allowed again
79       */
80      public static final Pattern CC_ATTRS_METHOD_EXPRESSION_REGEX
81              = Pattern.compile("[^\\(]*[^\\w\\.\\(]cc\\.attrs\\.[^\\.]*(\\(.*)?");
82      
83      private static final String CC = "cc";
84      
85      private static final String CC_ATTRS = "cc.attrs";
86      
87      public static final String CC_FIND_COMPONENT_EXPRESSION = "oam.CC_FIND_COMPONENT_EXPRESSION";
88      
89      /**
90       * private constructor
91       */
92      private CompositeComponentELUtils()
93      {
94          // no instantiation of this class
95      }
96      
97      /**
98       * Try to find a composite component on the composite component stack
99       * and using UIComponent.getCurrentCompositeComponent() based on the 
100      * location of the facelet page that generated the composite component.
101      * @param facesContext
102      * @param location
103      * @return
104      */
105     public static UIComponent getCompositeComponentBasedOnLocation(final FacesContext facesContext, 
106             final Location location)
107     {
108         //1 Use getCurrentComponent and getCurrentCompositeComponent to look on the component stack
109         UIComponent currentCompositeComponent = UIComponent.getCurrentCompositeComponent(facesContext);
110         
111         //1.1 Use getCurrentCompositeComponent first!
112         if (currentCompositeComponent != null)
113         {
114             Location componentLocation = (Location) currentCompositeComponent.getAttributes().get(LOCATION_KEY);
115             if (componentLocation != null 
116                     && componentLocation.getPath().equals(location.getPath()))
117             {
118                 return currentCompositeComponent;
119             }
120         }
121 
122         UIComponent currentComponent = UIComponent.getCurrentComponent(facesContext);
123         
124         if (currentComponent == null)
125         {
126             // Cannot found any component, because we don't have any reference!
127             return null;
128         }
129 
130         //2. Look on the stack using a recursive algorithm.
131         UIComponent matchingCompositeComponent
132                 = lookForCompositeComponentOnStack(facesContext, location, currentComponent);
133         
134         if (matchingCompositeComponent != null)
135         {
136             return matchingCompositeComponent;
137         }
138         
139         //2. Try to find it using UIComponent.getCurrentCompositeComponent(). 
140         // This one will look the direct parent hierarchy of the component,
141         // to see if the composite component can be found.
142         if (currentCompositeComponent != null)
143         {
144             currentComponent = currentCompositeComponent;
145         }
146         else
147         {
148             //Try to find the composite component looking directly the parent
149             //ancestor of the current component
150             //currentComponent = UIComponent.getCurrentComponent(facesContext);
151             boolean found = false;
152             while (currentComponent != null && !found)
153             {
154                 String findComponentExpr = (String) currentComponent.getAttributes().get(CC_FIND_COMPONENT_EXPRESSION);
155                 if (findComponentExpr != null)
156                 {
157                     UIComponent foundComponent = facesContext.getViewRoot().findComponent(findComponentExpr);
158                     if (foundComponent != null)
159                     {
160                         Location foundComponentLocation = (Location) currentComponent.getAttributes().get(LOCATION_KEY);
161                         if (foundComponentLocation != null 
162                                 && foundComponentLocation.getPath().equals(location.getPath()))
163                         {
164                             return foundComponent;
165                         }
166                         else
167                         {
168                             while (foundComponent != null)
169                             {
170                                 Location componentLocation
171                                         = (Location) foundComponent.getAttributes().get(LOCATION_KEY);
172                                 if (componentLocation != null 
173                                         && componentLocation.getPath().equals(location.getPath()))
174                                 {
175                                     return foundComponent;
176                                 }
177                                 // get the composite component's parent
178                                 foundComponent = UIComponent.getCompositeComponentParent(foundComponent);
179                             }
180                         }
181                     }
182                 }
183 
184                 if (UIComponent.isCompositeComponent(currentComponent))
185                 {
186                     found = true;
187                 }
188                 else
189                 {
190                     currentComponent = currentComponent.getParent();
191                 }
192             }
193         }
194         
195         //if currentComponent != null means we have a composite component that we can check
196         //Use UIComponent.getCompositeComponentParent() to traverse here.
197         while (currentComponent != null)
198         {
199             Location componentLocation = (Location) currentComponent.getAttributes().get(LOCATION_KEY);
200             if (componentLocation != null 
201                     && componentLocation.getPath().equals(location.getPath()))
202             {
203                 return currentComponent;
204             }
205             // get the composite component's parent
206             currentComponent = UIComponent.getCompositeComponentParent(currentComponent);
207         }
208         
209         // not found
210         return null;
211     }
212     
213     private static UIComponent lookForCompositeComponentOnStack(final FacesContext facesContext,
214                                                                 final Location location,
215                                                                 UIComponent currentComponent)
216     {
217         if (UIComponent.isCompositeComponent(currentComponent))
218         {
219             Location componentLocation = (Location) currentComponent.getAttributes().get(LOCATION_KEY);
220             if (componentLocation != null 
221                     && componentLocation.getPath().equals(location.getPath()))
222             {
223                 return currentComponent;
224             }
225         }
226         currentComponent.popComponentFromEL(facesContext);
227         try
228         {
229             UIComponent c = UIComponent.getCurrentComponent(facesContext);
230             if (c != null)
231             {
232                 return lookForCompositeComponentOnStack( facesContext, location, c);
233             }
234             else
235             {
236                 return null;
237             }
238         }
239         finally
240         {
241             currentComponent.pushComponentToEL(facesContext, currentComponent);
242         }
243     }
244 
245     private static int getCCLevel(UIComponent component)
246     {
247         Integer ccLevel = (Integer) component.getAttributes().get(LEVEL_KEY);
248         if (ccLevel == null)
249         {
250             return 0;
251         }
252         return ccLevel.intValue();
253     }
254     
255     /**
256      * Same as getCompositeComponentBasedOnLocation(final FacesContext facesContext, final Location location),
257      * but takes into account the ccLevel to resolve the composite component. 
258      * 
259      * @param facesContext
260      * @param location
261      * @param ccLevel
262      * @return 
263      */
264     public static UIComponent getCompositeComponentBasedOnLocation(final FacesContext facesContext, 
265             final Location location, int ccLevel)
266     {
267         //1 Use getCurrentComponent and getCurrentCompositeComponent to look on the component stack
268         UIComponent currentCompositeComponent = UIComponent.getCurrentCompositeComponent(facesContext);
269         
270         //1.1 Use getCurrentCompositeComponent first!
271         if (currentCompositeComponent != null)
272         {
273             Location componentLocation = (Location) currentCompositeComponent.getAttributes().get(LOCATION_KEY);
274             if (componentLocation != null 
275                     && componentLocation.getPath().equals(location.getPath()) && 
276                     (ccLevel == getCCLevel(currentCompositeComponent)) )
277             {
278                 return currentCompositeComponent;
279             }
280         }
281 
282         UIComponent currentComponent = UIComponent.getCurrentComponent(facesContext);
283         
284         if (currentComponent == null)
285         {
286             // Cannot found any component, because we don't have any reference!
287             return null;
288         }
289         
290         //2. Look on the stack using a recursive algorithm.
291         UIComponent matchingCompositeComponent
292                 = lookForCompositeComponentOnStack(facesContext, location, ccLevel, currentComponent);
293         
294         if (matchingCompositeComponent != null)
295         {
296             return matchingCompositeComponent;
297         }
298         
299         //2. Try to find it using UIComponent.getCurrentCompositeComponent(). 
300         // This one will look the direct parent hierarchy of the component,
301         // to see if the composite component can be found.
302         if (currentCompositeComponent != null)
303         {
304             currentComponent = currentCompositeComponent;
305         }
306         else
307         {
308             //Try to find the composite component looking directly the parent
309             //ancestor of the current component
310             //currentComponent = UIComponent.getCurrentComponent(facesContext);
311             boolean found = false;
312             while (currentComponent != null && !found)
313             {
314                 String findComponentExpr = (String) currentComponent.getAttributes().get(CC_FIND_COMPONENT_EXPRESSION);
315                 if (findComponentExpr != null)
316                 {
317                     UIComponent foundComponent = facesContext.getViewRoot().findComponent(findComponentExpr);
318                     if (foundComponent != null)
319                     {
320                         Location foundComponentLocation = (Location) currentComponent.getAttributes().get(LOCATION_KEY);
321                         if (foundComponentLocation != null 
322                                 && foundComponentLocation.getPath().equals(location.getPath()) &&
323                                 ccLevel == getCCLevel(foundComponent))
324                         {
325                             return foundComponent;
326                         }
327                         else
328                         {
329                             while (foundComponent != null)
330                             {
331                                 Location componentLocation
332                                         = (Location) foundComponent.getAttributes().get(LOCATION_KEY);
333                                 if (componentLocation != null 
334                                         && componentLocation.getPath().equals(location.getPath()) &&
335                                         ccLevel == getCCLevel(foundComponent))
336                                 {
337                                     return foundComponent;
338                                 }
339                                 // get the composite component's parent
340                                 foundComponent = UIComponent.getCompositeComponentParent(foundComponent);
341                             }
342                         }
343                     }
344                 }
345 
346                 if (UIComponent.isCompositeComponent(currentComponent))
347                 {
348                     found = true;
349                 }
350                 else
351                 {
352                     currentComponent = currentComponent.getParent();
353                 }
354             }
355         }
356         
357         //if currentComponent != null means we have a composite component that we can check
358         //Use UIComponent.getCompositeComponentParent() to traverse here.
359         while (currentComponent != null)
360         {
361             Location componentLocation = (Location) currentComponent.getAttributes().get(LOCATION_KEY);
362             if (componentLocation != null 
363                     && componentLocation.getPath().equals(location.getPath()) &&
364                     ccLevel == getCCLevel(currentComponent))
365             {
366                 return currentComponent;
367             }
368             // get the composite component's parent
369             currentComponent = UIComponent.getCompositeComponentParent(currentComponent);
370         }
371         
372         // not found
373         return null;
374     }
375 
376     private static UIComponent lookForCompositeComponentOnStack(final FacesContext facesContext,
377                                                                 final Location location, int ccLevel,
378                                                                 UIComponent currentComponent)
379     {
380         if (UIComponent.isCompositeComponent(currentComponent))
381         {
382             Location componentLocation = (Location) currentComponent.getAttributes().get(LOCATION_KEY);
383             if (componentLocation != null 
384                     && componentLocation.getPath().equals(location.getPath()) &&
385                     (ccLevel == getCCLevel(currentComponent)) )
386             {
387                 return currentComponent;
388             }
389         }
390         currentComponent.popComponentFromEL(facesContext);
391         try
392         {
393             UIComponent c = UIComponent.getCurrentComponent(facesContext);
394             if (c != null)
395             {
396                 return lookForCompositeComponentOnStack( facesContext, location, ccLevel, c);
397             }
398             else
399             {
400                 return null;
401             }
402         }
403         finally
404         {
405             currentComponent.pushComponentToEL(facesContext, currentComponent);
406         }
407     }
408     
409     /**
410      * Trys to get the composite component using getCompositeComponentBasedOnLocation()
411      * and saves it in an attribute on the FacesContext, which is then used by 
412      * CompositeComponentImplicitObject.
413      * @param facesContext
414      * @param location
415      */
416     public static void saveCompositeComponentForResolver(FacesContext facesContext, Location location, int ccLevel)
417     {
418         UIComponent cc = ccLevel > 0 ? getCompositeComponentBasedOnLocation(facesContext, location, ccLevel)
419                 : getCompositeComponentBasedOnLocation(facesContext, location);
420         List<UIComponent> list = (List<UIComponent>) facesContext.getAttributes().get(CURRENT_COMPOSITE_COMPONENT_KEY);
421         if (list == null)
422         {
423             list = new ArrayList<UIComponent>();
424             facesContext.getAttributes().put(CURRENT_COMPOSITE_COMPONENT_KEY, list);
425         }
426         list.add(cc);
427     }
428     
429     /**
430      * Removes the composite component from the attribute map of the FacesContext.
431      * @param facesContext
432      */
433     public static void removeCompositeComponentForResolver(FacesContext facesContext)
434     {
435         List<UIComponent> list = (List<UIComponent>) facesContext.getAttributes().get(CURRENT_COMPOSITE_COMPONENT_KEY);
436         if (list != null)
437         {
438             list.remove(list.size()-1);
439         }
440     }
441     
442     /**
443      * Tests if the expression refers to the current composite component: #{cc}
444      * @return
445      */
446     public static boolean isCompositeComponentExpression(String expression)
447     {
448         if (expression.contains(CC))
449         {
450             return CC_EXPRESSION_REGEX.matcher(expression).matches();
451         }
452         else
453         {
454             return false;
455         }
456     }
457     
458     /**
459      * Tests if cc.attrs is used as a method expression in an expression String. This means 
460      * cc.attrs must occur, must stand before a '(', because otherwise it would be a method parameter 
461      * (EL 2.2), and there must be no '.' after cc.attrs unless there is a left parenthesis
462      * before it (e.g. #{cc.attrs.method(bean.parameter)}).
463      * @param expression
464      * @return
465      */
466     public static boolean isCompositeComponentAttrsMethodExpression(String expression)
467     {
468         if (expression.contains(CC_ATTRS))
469         {
470             return CC_ATTRS_METHOD_EXPRESSION_REGEX.matcher(expression).matches();
471         }
472         else
473         {
474             return false;
475         }
476     }
477     
478 }