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.trinidad.util;
20
21 import java.util.Date;
22 import java.util.Locale;
23 import java.util.Map;
24 import java.util.TimeZone;
25
26 import javax.faces.component.NamingContainer;
27 import javax.faces.component.UIComponent;
28
29 import javax.faces.component.UIViewRoot;
30
31 import javax.faces.component.visit.VisitContext;
32
33 import org.apache.myfaces.trinidad.component.UIXComponent;
34 import javax.faces.context.FacesContext;
35
36 import org.apache.myfaces.trinidad.component.FlattenedComponent;
37 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
38
39 /**
40 * Utility functions used by the Apache Trinidad components.
41 * <p>
42 * @version $Name: $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/util/ComponentUtils.java#0 $) $Date: 10-nov-2005.19:08:37 $
43 */
44 public final class ComponentUtils
45 {
46 private ComponentUtils()
47 {
48 }
49
50 /**
51 * Utility method for component code that resolves an Object,
52 * returning a default value if the value is null.
53 */
54 public static Object resolveObject(
55 Object value,
56 Object defaultValue
57 )
58 {
59 return (value != null)
60 ? value
61 : defaultValue;
62 }
63
64
65 /**
66 * Utility method for component code that transforms Object->boolean.
67 */
68 public static boolean resolveBoolean(
69 Object value,
70 boolean defaultValue
71 )
72 {
73 if (defaultValue)
74 return !Boolean.FALSE.equals(value);
75 else
76 return Boolean.TRUE.equals(value);
77 }
78
79 /**
80 * Utility method for component code that transforms Object->boolean.
81 */
82 public static boolean resolveBoolean(
83 Object value
84 )
85 {
86 return Boolean.TRUE.equals(value);
87 }
88
89
90 /**
91 * Utility method for component code that transforms an Object
92 * (which must be a java.lang.Number) into an int.
93 */
94 public static int resolveInteger(
95 Object value
96 )
97 {
98 return resolveInteger(value, 0);
99 }
100
101 /**
102 * Utility method for component code that transforms an Object
103 * (which must be a java.lang.Number) into an int.
104 */
105 public static int resolveInteger(
106 Object value,
107 int defaultValue
108 )
109 {
110 return (value != null)
111 ? ((Number)value).intValue()
112 : defaultValue;
113 }
114
115
116 /**
117 * Utility method for component code that transforms an Object
118 * (which must be a java.lang.Number) into a long.
119 */
120 public static long resolveLong(
121 Object value
122 )
123 {
124 return resolveLong(value, 0);
125 }
126
127 /**
128 * Utility method for component code that transforms an Object
129 * (which must be a java.lang.Number) into a long.
130 */
131 public static long resolveLong(
132 Object value,
133 long defaultValue
134 )
135 {
136 return (value != null)
137 ? ((Number)value).longValue()
138 : defaultValue;
139 }
140
141 /**
142 * Utility method for component code that transforms an Object
143 * (which must be a java.lang.Number) into a double.
144 */
145 public static double resolveDouble(
146 Object value
147 )
148 {
149 return resolveDouble(value, 0);
150 }
151
152 /**
153 * Utility method for component code that transforms an Object
154 * (which must be a java.lang.Number) into a double.
155 */
156 public static double resolveDouble(
157 Object value,
158 double defaultValue
159 )
160 {
161 return (value != null)
162 ? ((Number)value).doubleValue()
163 : defaultValue;
164 }
165
166
167 /**
168 * Utility method for component code that transforms Character->character.
169 */
170 public static char resolveCharacter(
171 Character value
172 )
173 {
174 return resolveCharacter(value, '\u0000');
175 }
176
177 /**
178 * Utility method for component code that transforms Character->character.
179 */
180 public static char resolveCharacter(
181 Character value,
182 char defaultValue
183 )
184 {
185 return (value != null)
186 ? value.charValue()
187 : defaultValue;
188 }
189
190
191 /**
192 * Utility method for component code that transforms Object->Number.
193 */
194 public static Number resolveNumber(
195 Object value
196 )
197 {
198 return resolveNumber(value, null);
199 }
200
201
202 /**
203 * Utility method for component code that transforms Object->Number.
204 */
205 public static Number resolveNumber(
206 Object value,
207 Number defaultValue
208 )
209 {
210 return (value != null)
211 ? (Number) value
212 : defaultValue;
213 }
214
215
216 /**
217 * Utility method for component code that transforms Object->String.
218 */
219 public static String resolveString(
220 Object value
221 )
222 {
223 return resolveString(value, false);
224 }
225
226 /**
227 * Utility method for component code that transforms Object->String.
228 * If treatEmptyStringAsNull is true, null is returned for empty string.
229 */
230 public static String resolveString(
231 Object value,
232 boolean treatEmptyStringAsNull
233 )
234 {
235 if (value == null)
236 {
237 return null;
238 }
239
240 String strValue = value.toString();
241 if (treatEmptyStringAsNull && strValue.trim().isEmpty())
242 {
243 return null;
244 }
245
246 return strValue;
247 }
248
249 /**
250 * Utility method for component code that transforms Object->String.
251 */
252 public static String resolveString(
253 Object value,
254 String defaultValue
255 )
256 {
257 return (value != null)
258 ? value.toString()
259 : defaultValue;
260 }
261
262 /**
263 * Utility method for code that transforms Object->String[]
264 */
265 public static String[] resolveStringArray(
266 Object value)
267 {
268 return resolveStringArray(value, null);
269 }
270
271 /**
272 * Utility method for code that transforms Object->String[]
273 */
274 public static String[] resolveStringArray(
275 Object value,
276 String[] defaultValue)
277 {
278 return (value != null)
279 ? (String[]) value
280 : defaultValue;
281 }
282
283 /**
284 * Utility method for code that transforms Object->Date
285 */
286 public static Date resolveDate(Object value)
287 {
288 return resolveDate(value, null);
289 }
290
291 /**
292 * Utility method for code that transforms Object->Date
293 */
294 public static Date resolveDate(
295 Object value,
296 Date defaultValue)
297 {
298 return (value != null)
299 ? (Date) value
300 : defaultValue;
301 }
302
303 public static TimeZone resolveTimeZone(
304 Object value
305 )
306 {
307 return resolveTimeZone(value, null);
308 }
309
310 public static TimeZone resolveTimeZone(
311 Object value,
312 TimeZone defaultValue
313 )
314 {
315 return (value != null)
316 ? (TimeZone) value
317 : defaultValue;
318 }
319
320 public static Locale resolveLocale(
321 Object value
322 )
323 {
324 return resolveLocale(value, null);
325 }
326
327 public static Locale resolveLocale(
328 Object value,
329 Locale defaultValue
330 )
331 {
332 return (value != null)
333 ? (Locale) value
334 : defaultValue;
335 }
336
337 /**
338 * @param visitContext
339 * @return <code>true</code> if this is a non-iterating visit.
340 */
341 public static boolean isSkipIterationVisit(VisitContext visitContext)
342 {
343 FacesContext context = visitContext.getFacesContext();
344 Map<Object, Object> attrs = context.getAttributes();
345 Object skipIteration = attrs.get("javax.faces.visit.SKIP_ITERATION");
346
347 return Boolean.TRUE.equals(skipIteration);
348 }
349
350 /**
351 * Gets the root cause of an exception.
352 * Keeps unwrapping the given throwable until the root cause is found.
353 */
354 public static Throwable unwrap(Throwable t)
355 {
356 while(true)
357 {
358 Throwable unwrap = t.getCause();
359 if (unwrap == null)
360 break;
361 t = unwrap;
362 }
363 return t;
364 }
365
366
367 /**
368 * Find a component relative to another.
369 * <p>
370 * The relative ID must account for NamingContainers. If the component is already inside
371 * of a naming container, you can use a single colon to start the search from the root,
372 * or multiple colons to move up through the NamingContainers - "::" will
373 * pop out of the current naming container, ":::" will pop out of two
374 * naming containers, etc.
375 * </p>
376 *
377 * @param from the component to search relative to
378 * @param scopedId the relative id path from the 'from' component to the
379 * component to find
380 * @return the component if found, null otherwise
381 * @see org.apache.myfaces.trinidad.render.RenderUtils#getRelativeId
382 * @see javax.faces.component.UIComponent#findComponent
383 */
384 public static UIComponent findRelativeComponent(
385 UIComponent from,
386 String scopedId)
387 {
388 if (from == null)
389 return null;
390 UIComponent originalFrom = from;
391 String originalRelativeId = scopedId;
392
393 int idLength = scopedId.length();
394 // Figure out how many colons
395 int colonCount = 0;
396 while (colonCount < idLength)
397 {
398 if (scopedId.charAt(colonCount) != NamingContainer.SEPARATOR_CHAR)
399 break;
400 colonCount++;
401 }
402
403 // colonCount == 0: fully relative
404 // colonCount == 1: absolute (still normal findComponent syntax)
405 // colonCount > 1: for each extra colon after 1, pop out of
406 // the naming container (to the view root, if naming containers run out)
407 if (colonCount > 1)
408 {
409 scopedId = scopedId.substring(colonCount);
410
411 // if the component is not a NamingContainer, then we need to
412 // get the component's naming container and set this as the 'from'.
413 // this way we'll pop out of the component's
414 // naming container if there is are multiple colons.
415 if (!(from instanceof NamingContainer))
416 {
417 from = _getParentNamingContainerOrViewRoot(from);
418 }
419
420 // pop out of the naming containers if there are multiple colons
421 for (int j = 1; j < colonCount; j++)
422 {
423 from = _getParentNamingContainerOrViewRoot(from);
424 }
425 }
426
427 UIComponent found = from.findComponent(scopedId);
428 if (found != null)
429 return found;
430 else
431 {
432 // try the old way for backward compatability as far as it differed,
433 // which is only if the 'from' was not a NamingContainer.
434 if (!(originalFrom instanceof NamingContainer))
435 return _findRelativeComponentDeprecated(originalFrom, originalRelativeId);
436 else
437 return null;
438 }
439
440 }
441
442 /**
443 * Gets the scoped identifier for the target component. The scoping will be
444 * within a subtree rooted by the supplied base component. If the supplied
445 * base component were to be the view root, the returned id will be the
446 * absolute id and hence prefixed with NamingContainer.SEPARATOR_CHARACTER.
447 *
448 * This algorithm reverse matches that of UIComponent.findComponent().
449 * In other words, the scoped id returned by this method can be safely used
450 * in calls to findComponent() on the baseComponent, if it were to be
451 * enclosing the targetComponent.
452 *
453 * This method assumes that the supplied baseComponent definitely encloses the
454 * targetComponent, return value is not reliable if this is not the case.
455 *
456 * Examples of id returned: ':foo:bar:baz'/'foo:baz'/'foo'
457 *
458 * @param targetComponent The component for which the scoped id needs to be
459 * determined.
460 * @param baseComponent The component relative to which the scoped id for the
461 * targetComponent needs to be determined.
462 * @return The scoped id for target component. Returns null if the supplied
463 * targetComponent was null or did not have an id.
464 */
465 public static String getScopedIdForComponent(
466 UIComponent targetComponent,
467 UIComponent baseComponent)
468 {
469 return _getScopedIdForComponentImpl(targetComponent, baseComponent, false);
470 }
471
472 /**
473 * Gets the logical scoped identifier for the target component. The scoping will be
474 * within a subtree rooted by the supplied base component. The subtree will be computed in the
475 * context of the document or documents where the components were defined. If the supplied
476 * base component were to be the view root, the returned id will be the
477 * absolute id and hence prefixed with NamingContainer.SEPARATOR_CHARACTER.
478 *
479 * This algorithm reverse matches that of UIComponent.findComponent().
480 * In other words, the scoped id returned by this method can be safely used
481 * in calls to findComponent() on the baseComponent, if it were to be
482 * enclosing the targetComponent.
483 *
484 * This method assumes that the supplied baseComponent definitely encloses the
485 * targetComponent, return value is not reliable if this is not the case.
486 *
487 * Examples of id returned: ':foo:bar:baz'/'foo:baz'/'foo'
488 *
489 * @param targetComponent The component for which the scoped id needs to be
490 * determined.
491 * @param baseComponent The component relative to which the scoped id for the
492 * targetComponent needs to be determined.
493 * @return The scoped id for target component. Returns null if the supplied
494 * targetComponent was null or did not have an id.
495 */
496 public static String getLogicalScopedIdForComponent(
497 UIComponent targetComponent,
498 UIComponent baseComponent)
499 {
500 return _getScopedIdForComponentImpl(targetComponent, baseComponent, true);
501 }
502
503 /**
504 * Returns scoped id for a component
505 * @param targetComponent The component for which the scoped id needs to be
506 * determined.
507 * @param baseComponent The component relative to which the scoped id for the
508 * targetComponent needs to be determined.
509 * @param isLogical true if a logical scoped id (the id in the context of the document where targetComponent was defined)
510 * should be returned, false otherwise
511 * @return The scoped id for target component. Returns null if the supplied
512 * targetComponent was null or did not have an id.
513 */
514 private static String _getScopedIdForComponentImpl(
515 UIComponent targetComponent,
516 UIComponent baseComponent,
517 boolean isLogical)
518 {
519 String targetComponentId = targetComponent.getId();
520
521 if (targetComponent == null ||
522 targetComponentId == null ||
523 targetComponentId.length() == 0)
524 return null;
525
526 // Optimize when both arguments are the same
527 if (targetComponent.equals(baseComponent))
528 return targetComponentId;
529
530 StringBuilder builder = new StringBuilder(100);
531
532 // Add a leading ':' if the baseComponent is the view root
533 if (baseComponent instanceof UIViewRoot)
534 builder.append(NamingContainer.SEPARATOR_CHAR);
535
536 _buildScopedId(targetComponent, baseComponent, builder, isLogical);
537
538 return builder.toString();
539 }
540
541 /**
542 * Returns the nearest ancestor component, skipping over any
543 * flattening components.
544 *
545 * @param context the FacesContext
546 * @param component the UIComponent
547 * @return the first ancestor component that is not a FlattenedComponent
548 * that is actively flattening its children or null if no such ancestor
549 * component is found.
550 * @see org.apache.myfaces.trinidad.component.FlattenedComponent
551 */
552 public static UIComponent getNonFlatteningAncestor(
553 FacesContext context,
554 UIComponent component)
555 {
556 UIComponent parent = component.getParent();
557
558 while (parent != null)
559 {
560 if (!_isFlattening(context, parent))
561 return parent;
562
563 parent = parent.getParent();
564 }
565
566 return null;
567 }
568
569 private static boolean _isFlattening(FacesContext context, UIComponent component)
570 {
571 return ((component instanceof FlattenedComponent) &&
572 ((FlattenedComponent)component).isFlatteningChildren(context));
573 }
574
575 /**
576 * Builds the scoped id. Adds the naming container's id and the separator char
577 * in a recursive fashion.
578 * @param targetComponent The component for which the scoped id needs to be
579 * built.
580 * @param baseComponent The component relative to which the scoped id for the
581 * targetComponent needs to be built.
582 * @param builder The StringBuilder which is to store the scoped id.
583 * @param isLogical true if the logical scoped id should be returned, false otherwise
584 * @return The String value of the scoped id
585 */
586 private static void _buildScopedId(
587 UIComponent targetComponent,
588 UIComponent baseComponent,
589 StringBuilder builder,
590 boolean isLogical)
591 {
592 UIComponent namingContainer =
593 _getParentNamingContainer(targetComponent, baseComponent, isLogical);
594
595 if (namingContainer != null)
596 {
597 _buildScopedId(namingContainer, baseComponent, builder, isLogical);
598 builder.append(NamingContainer.SEPARATOR_CHAR);
599 }
600
601 builder.append(targetComponent.getId());
602 }
603
604 /**
605 * Returns the naming container of the component. This method makes sure that
606 * we don't go beyond the a supplied base component.
607 * @param component the UIComponent
608 * @param baseComponent The component to limit the search up to.
609 * @param isLogical true if logical parent hierarchy should be used, false otherwise
610 * @return the naming container of the component which has to be in the
611 * subtree rooted by the baseComponent. Returns null if no such ancestor
612 * naming container component exists.
613 */
614 private static UIComponent _getParentNamingContainer(
615 UIComponent component,
616 UIComponent baseComponent,
617 boolean isLogical)
618 {
619 // Optimize when both arguments are the same - could happen due to recursion
620 // in _buildScopedId()
621 if (component.equals(baseComponent))
622 return null;
623
624 UIComponent checkedParent = component;
625
626 do
627 {
628 checkedParent = isLogical ? UIXComponent.getLogicalParent(checkedParent) : checkedParent.getParent();
629
630 if (checkedParent == null)
631 break;
632
633 if (checkedParent instanceof NamingContainer)
634 break;
635
636 // We hit the base component, abort.
637 if (checkedParent == baseComponent)
638 return null;
639
640 } while (true);
641
642 return checkedParent;
643 }
644
645 // given a component, get its naming container. If the component
646 // is a naming container, it will get its naming container.
647 // if no parent naming containers exist, it stops at the ViewRoot.
648 private static UIComponent _getParentNamingContainerOrViewRoot (
649 UIComponent from)
650 {
651 while (from.getParent() != null)
652 {
653 from = from.getParent();
654 if (from instanceof NamingContainer)
655 break;
656 }
657 return from;
658 }
659
660 /**
661 * Find a component relative to another.
662 * This method is the same as the 'old' public findRelativeComponent.
663 * This method is around so that the
664 * new findRelativeComponent method is backward compatibility.
665 * <p>
666 * The relative ID must account for NamingContainers. If the component is already inside
667 * of a naming container, you can use a single colon to start the search from the root,
668 * or multiple colons to move up through the NamingContainers - "::" will search from
669 * the parent naming container, ":::" will search from the grandparent
670 * naming container, etc.
671 * </p>
672 *
673 * @param from the component to search relative to
674 * @param relativeId the relative path to the component to find
675 * @return the component if found, null otherwise
676 */
677 private static UIComponent _findRelativeComponentDeprecated(
678 UIComponent from,
679 String relativeId)
680 {
681 UIComponent originalFrom = from;
682 String originalRelativeId = relativeId;
683
684 int idLength = relativeId.length();
685 // Figure out how many colons
686 int colonCount = 0;
687 while (colonCount < idLength)
688 {
689 if (relativeId.charAt(colonCount) != NamingContainer.SEPARATOR_CHAR)
690 break;
691 colonCount++;
692 }
693
694 // colonCount == 0: fully relative
695 // colonCount == 1: absolute (still normal findComponent syntax)
696 // colonCount > 1: for each extra colon after 1, go up a naming container
697 // (to the view root, if naming containers run out)
698 if (colonCount > 1)
699 {
700 relativeId = relativeId.substring(colonCount);
701 for (int j = 1; j < colonCount; j++)
702 {
703 while (from.getParent() != null)
704 {
705 from = from.getParent();
706 if (from instanceof NamingContainer)
707 break;
708 }
709 }
710 }
711
712 UIComponent found = from.findComponent(relativeId);
713 if (found != null)
714 {
715 _LOG.warning("DEPRECATED_RELATIVE_ID_SYNTAX",
716 new Object[] {originalRelativeId, originalFrom});
717 }
718 return found;
719 }
720
721 static private final TrinidadLogger _LOG =
722 TrinidadLogger.createTrinidadLogger(ComponentUtils.class);
723 }