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$)
33   * @version $Revision$ $Date$
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     public static UIComponent getCompositeComponentBasedOnLocation(final FacesContext facesContext, 
256             UIComponent baseComponent, final Location location)
257     {
258         UIComponent currentComponent = baseComponent;
259         while (currentComponent != null)
260         {
261             Location componentLocation = (Location) currentComponent.getAttributes().get(
262                 LOCATION_KEY);
263             if (componentLocation != null 
264                     && componentLocation.getPath().equals(location.getPath()))
265             {
266                 return currentComponent;
267             }
268             // get the composite component's parent
269             currentComponent = UIComponent.getCompositeComponentParent(currentComponent);
270         }
271         return null;
272     }
273 
274     
275     /**
276      * Same as getCompositeComponentBasedOnLocation(final FacesContext facesContext, final Location location),
277      * but takes into account the ccLevel to resolve the composite component. 
278      * 
279      * @param facesContext
280      * @param location
281      * @param ccLevel
282      * @return 
283      */
284     public static UIComponent getCompositeComponentBasedOnLocation(final FacesContext facesContext, 
285             final Location location, int ccLevel)
286     {
287         //1 Use getCurrentComponent and getCurrentCompositeComponent to look on the component stack
288         UIComponent currentCompositeComponent = UIComponent.getCurrentCompositeComponent(facesContext);
289         
290         //1.1 Use getCurrentCompositeComponent first!
291         if (currentCompositeComponent != null)
292         {
293             Location componentLocation = (Location) currentCompositeComponent.getAttributes().get(LOCATION_KEY);
294             if (componentLocation != null 
295                     && componentLocation.getPath().equals(location.getPath()) && 
296                     (ccLevel == getCCLevel(currentCompositeComponent)) )
297             {
298                 return currentCompositeComponent;
299             }
300         }
301 
302         UIComponent currentComponent = UIComponent.getCurrentComponent(facesContext);
303         
304         if (currentComponent == null)
305         {
306             // Cannot found any component, because we don't have any reference!
307             return null;
308         }
309         
310         //2. Look on the stack using a recursive algorithm.
311         UIComponent matchingCompositeComponent
312                 = lookForCompositeComponentOnStack(facesContext, location, ccLevel, currentComponent);
313         
314         if (matchingCompositeComponent != null)
315         {
316             return matchingCompositeComponent;
317         }
318         
319         //2. Try to find it using UIComponent.getCurrentCompositeComponent(). 
320         // This one will look the direct parent hierarchy of the component,
321         // to see if the composite component can be found.
322         if (currentCompositeComponent != null)
323         {
324             currentComponent = currentCompositeComponent;
325         }
326         else
327         {
328             //Try to find the composite component looking directly the parent
329             //ancestor of the current component
330             //currentComponent = UIComponent.getCurrentComponent(facesContext);
331             boolean found = false;
332             while (currentComponent != null && !found)
333             {
334                 String findComponentExpr = (String) currentComponent.getAttributes().get(CC_FIND_COMPONENT_EXPRESSION);
335                 if (findComponentExpr != null)
336                 {
337                     UIComponent foundComponent = facesContext.getViewRoot().findComponent(findComponentExpr);
338                     if (foundComponent != null)
339                     {
340                         Location foundComponentLocation = (Location) currentComponent.getAttributes().get(LOCATION_KEY);
341                         if (foundComponentLocation != null 
342                                 && foundComponentLocation.getPath().equals(location.getPath()) &&
343                                 ccLevel == getCCLevel(foundComponent))
344                         {
345                             return foundComponent;
346                         }
347                         else
348                         {
349                             while (foundComponent != null)
350                             {
351                                 Location componentLocation
352                                         = (Location) foundComponent.getAttributes().get(LOCATION_KEY);
353                                 if (componentLocation != null 
354                                         && componentLocation.getPath().equals(location.getPath()) &&
355                                         ccLevel == getCCLevel(foundComponent))
356                                 {
357                                     return foundComponent;
358                                 }
359                                 // get the composite component's parent
360                                 foundComponent = UIComponent.getCompositeComponentParent(foundComponent);
361                             }
362                         }
363                     }
364                 }
365 
366                 if (UIComponent.isCompositeComponent(currentComponent))
367                 {
368                     found = true;
369                 }
370                 else
371                 {
372                     currentComponent = currentComponent.getParent();
373                 }
374             }
375         }
376         
377         //if currentComponent != null means we have a composite component that we can check
378         //Use UIComponent.getCompositeComponentParent() to traverse here.
379         while (currentComponent != null)
380         {
381             Location componentLocation = (Location) currentComponent.getAttributes().get(LOCATION_KEY);
382             if (componentLocation != null 
383                     && componentLocation.getPath().equals(location.getPath()) &&
384                     ccLevel == getCCLevel(currentComponent))
385             {
386                 return currentComponent;
387             }
388             // get the composite component's parent
389             currentComponent = UIComponent.getCompositeComponentParent(currentComponent);
390         }
391         
392         // not found
393         return null;
394     }
395 
396     private static UIComponent lookForCompositeComponentOnStack(final FacesContext facesContext,
397                                                                 final Location location, int ccLevel,
398                                                                 UIComponent currentComponent)
399     {
400         if (UIComponent.isCompositeComponent(currentComponent))
401         {
402             Location componentLocation = (Location) currentComponent.getAttributes().get(LOCATION_KEY);
403             if (componentLocation != null 
404                     && componentLocation.getPath().equals(location.getPath()) &&
405                     (ccLevel == getCCLevel(currentComponent)) )
406             {
407                 return currentComponent;
408             }
409         }
410         currentComponent.popComponentFromEL(facesContext);
411         try
412         {
413             UIComponent c = UIComponent.getCurrentComponent(facesContext);
414             if (c != null)
415             {
416                 return lookForCompositeComponentOnStack( facesContext, location, ccLevel, c);
417             }
418             else
419             {
420                 return null;
421             }
422         }
423         finally
424         {
425             currentComponent.pushComponentToEL(facesContext, currentComponent);
426         }
427     }
428     
429     /**
430      * Trys to get the composite component using getCompositeComponentBasedOnLocation()
431      * and saves it in an attribute on the FacesContext, which is then used by 
432      * CompositeComponentImplicitObject.
433      * @param facesContext
434      * @param location
435      */
436     public static void saveCompositeComponentForResolver(FacesContext facesContext, Location location, int ccLevel)
437     {
438         UIComponent cc = ccLevel > 0 ? getCompositeComponentBasedOnLocation(facesContext, location, ccLevel)
439                 : getCompositeComponentBasedOnLocation(facesContext, location);
440         List<UIComponent> list = (List<UIComponent>) facesContext.getAttributes().get(CURRENT_COMPOSITE_COMPONENT_KEY);
441         if (list == null)
442         {
443             list = new ArrayList<UIComponent>();
444             facesContext.getAttributes().put(CURRENT_COMPOSITE_COMPONENT_KEY, list);
445         }
446         list.add(cc);
447     }
448     
449     /**
450      * Removes the composite component from the attribute map of the FacesContext.
451      * @param facesContext
452      */
453     public static void removeCompositeComponentForResolver(FacesContext facesContext)
454     {
455         List<UIComponent> list = (List<UIComponent>) facesContext.getAttributes().get(CURRENT_COMPOSITE_COMPONENT_KEY);
456         if (list != null)
457         {
458             list.remove(list.size()-1);
459         }
460     }
461     
462     /**
463      * Tests if the expression refers to the current composite component: #{cc}
464      * @return
465      */
466     public static boolean isCompositeComponentExpression(String expression)
467     {
468         if (expression.contains(CC))
469         {
470             return CC_EXPRESSION_REGEX.matcher(expression).matches();
471         }
472         else
473         {
474             return false;
475         }
476     }
477     
478     /**
479      * Tests if cc.attrs is used as a method expression in an expression String. This means 
480      * cc.attrs must occur, must stand before a '(', because otherwise it would be a method parameter 
481      * (EL 2.2), and there must be no '.' after cc.attrs unless there is a left parenthesis
482      * before it (e.g. #{cc.attrs.method(bean.parameter)}).
483      * @param expression
484      * @return
485      */
486     public static boolean isCompositeComponentAttrsMethodExpression(String expression)
487     {
488         if (expression.contains(CC_ATTRS))
489         {
490             return CC_ATTRS_METHOD_EXPRESSION_REGEX.matcher(expression).matches();
491         }
492         else
493         {
494             return false;
495         }
496     }
497     
498 }