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 }