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.component;
20
21 import java.io.IOException;
22 import java.util.Collection;
23 import java.util.Iterator;
24
25 import javax.el.MethodExpression;
26
27 import javax.faces.FactoryFinder;
28 import javax.faces.application.Application;
29 import javax.faces.application.ApplicationFactory;
30 import javax.faces.application.ProjectStage;
31 import javax.faces.component.NamingContainer;
32 import javax.faces.component.StateHelper;
33 import javax.faces.component.UIComponent;
34 import javax.faces.component.UINamingContainer;
35 import javax.faces.component.UIPanel;
36 import javax.faces.component.visit.VisitCallback;
37 import javax.faces.component.visit.VisitContext;
38 import javax.faces.component.visit.VisitHint;
39 import javax.faces.component.visit.VisitResult;
40 import javax.faces.context.FacesContext;
41 import javax.faces.event.PhaseId;
42 import javax.faces.render.Renderer;
43
44 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
45 import org.apache.myfaces.trinidad.bean.FacesBean;
46 import org.apache.myfaces.trinidad.context.ComponentContextChange;
47 import org.apache.myfaces.trinidad.context.ComponentContextManager;
48 import org.apache.myfaces.trinidad.context.PartialPageContext;
49 import org.apache.myfaces.trinidad.context.RenderingContext;
50 import org.apache.myfaces.trinidad.context.RequestContext;
51 import org.apache.myfaces.trinidad.event.AttributeChangeListener;
52 import org.apache.myfaces.trinidad.logging.TrinidadLogger;
53 import org.apache.myfaces.trinidad.render.CoreRenderer;
54
55
56 /**
57 * Pure abstract base class for all UIX components.
58 */
59 @JSFComponent
60 abstract public class UIXComponent extends UIComponent
61 {
62 /**
63 * Helper function called by Renderers to iterate over a flattened view of a group of
64 * potentially FlattenedComponent instances rooted at a single child of the component to collect
65 * information about these children prior to encoding the children using
66 * <code>encodeFlattenedChild(FacesContext, ComponentProcessor, UIComponent, Object)</code>.
67 * <p>
68 * If the child is a FlattenedComponent, the <code>childProcessor</code> will
69 * be called on each of that FlattenedComponent's children, recursing if those children are
70 * themselves FlattenedComponents, otherwise, the <code>childProcessor</code> will be called on
71 * the child itself.
72 * <p>
73 * If the Renderer accidentally passes in the component to be processed instead of one
74 * of its children, the result will almost certainly be an infinite recursion and stack overflow.
75 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, Iterable, Object)
76 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, UIComponent, Object)
77 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, Iterable, Object)
78 * @see FlattenedComponent
79 */
80 public static <S> boolean processFlattenedChildren(
81 FacesContext context,
82 ComponentProcessor<S> childProcessor,
83 UIComponent child,
84 S callbackContext) throws IOException
85 {
86 return processFlattenedChildren(context,
87 new ComponentProcessingContext(),
88 childProcessor,
89 child,
90 callbackContext);
91 }
92
93 /**
94 * Helper function called by Renderers to encode a flattened view of a group of
95 * potentially FlattenedComponent instances rooted at a single child of the component,
96 * invoking the <code>childProcessor</code> with its
97 * <code>callbackContext</code> on each renderable instance. This method must be called
98 * when the childProcessor is actually encoding and the childProcessor must not attempt
99 * to encode the same component instances more than once per request.
100 * <p>
101 * If a Renderer needs to
102 * collect information about its possibly flattened children before calling
103 * <code>encodeFlattenedChild(FacesContext, ComponentProcessor, UIComponent, Object)</code>,
104 * it should call <code>processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object)</code>
105 * to collect the information.
106 * <p>
107 * If the child is a FlattenedComponent, the <code>childProcessor</code> will
108 * be called on each of that FlattenedComponent's children, recursing if those children are
109 * themselves FlattenedComponents, otherwise, the <code>childProcessor</code> will be called on
110 * the child itself.
111 * <p>
112 * FlattenedComponents that wish to check whether they are processed for the purpose of
113 * encoding can check the ProcessingHints of the ComponentProcessingContext for the
114 * presence of <code>PROCESS_FOR_ENCODING hint</code>.
115 * <p>
116 * If the Renderer accidentally passes in the component to be encoded instead of one
117 * of its children, the result will almost certainly be an infinite recursion and stack overflow.
118 * @return <code>true</code> If any children were processed
119 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object)
120 * @see FlattenedComponent
121 */
122 public static <S> boolean encodeFlattenedChild(
123 FacesContext context,
124 ComponentProcessor<S> childProcessor,
125 UIComponent child,
126 S callbackContext) throws IOException
127 {
128 ComponentProcessingContext processingContext = new ComponentProcessingContext();
129 processingContext.__setIsRendering();
130
131 return processFlattenedChildren(context,
132 processingContext,
133 childProcessor,
134 child,
135 callbackContext);
136 }
137
138 /**
139 * Helper function called by FlattenedComponent to iterate over a flattened view of a group of
140 * potentially FlattenedComponent instances rooted at a single child of the FlattenedComponent,
141 * invoking the <code>childProcessor</code> with its
142 * <code>callbackContext</code> on each renderable instance.
143 * <p>
144 * If the child is a FlattenedComponent, the <code>childProcessor</code> will
145 * be called on each of that FlattenedComponent's children, recursing if those children are
146 * themselves FlattenedComponents, otherwise, the <code>childProcessor</code> will be called on
147 * the child itself.
148 * <p>
149 * This method is typically used to flatten the contents of a facet of the FlattenedComponent.
150 * If the FlattenedComponent accidentally passes in itself instead of one
151 * of its children, the result will almost certainly be an infinite recursion and stack overflow.
152 * @return <code>true</code> If any children were processed
153 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object)
154 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, Iterable, Object)
155 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, Iterable, Object)
156 * @see FlattenedComponent
157 */
158 public static <S> boolean processFlattenedChildren(
159 FacesContext context,
160 ComponentProcessingContext cpContext,
161 ComponentProcessor<S> childProcessor,
162 UIComponent child,
163 S callbackContext) throws IOException
164 {
165 if (child.isRendered())
166 {
167 // component is an action FlattenedComponent. Ask it to flatten its children
168 if ((child instanceof FlattenedComponent) &&
169 ((FlattenedComponent)child).isFlatteningChildren(context))
170 {
171 return ((FlattenedComponent)child).processFlattenedChildren(context,
172 cpContext,
173 childProcessor,
174 callbackContext);
175 }
176 else
177 {
178 boolean processed = true;
179 child.pushComponentToEL(context, null);
180
181 try
182 {
183 if (isFlattenableCoreComponent(child))
184 {
185 processed =
186 processFlattenedChildren(context, cpContext, childProcessor,
187 child.getChildren(),
188 callbackContext);
189 }
190 else
191 {
192 try
193 {
194 // not a FlattenedComponent, pass the component directly to the ComponentProcessor
195 childProcessor.processComponent(context, cpContext, child,
196 callbackContext);
197 }
198 finally
199 {
200 // if startDepth is > 0, only the first visible child will be marked as starting a group
201 cpContext.resetStartDepth();
202 }
203 }
204 }
205 finally
206 {
207 child.popComponentFromEL(context);
208 }
209
210 return processed;
211 }
212 }
213 else
214 {
215 // component not rendered
216 return false;
217 }
218 }
219
220 /**
221 * Helper function called by Renderers to iterate over a flattened view of the
222 * children, potentially containing FlattenedComponents, of the component the Renderer is
223 * encoding, invoking the <code>childProcessor</code> with its
224 * <code>callbackContext</code> on each renderable instance.
225 * <p>
226 * For each FlattenedComponent child, the <code>childProcessor</code> will
227 * be called on each of that FlattenedComponent's children, recursing if those children are
228 * themselves FlattenedComponents, otherwise, the <code>childProcessor</code> will be called on
229 * the child itself.
230 * <p>
231 * This method is typically used to flatten the children of the FlattenedComponent to
232 * be encoded.
233 * @return <code>true</code> If any children were processed
234 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object)
235 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, UIComponent, Object)
236 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, Iterable, Object)
237 * @see FlattenedComponent
238 */
239 public static <S> boolean processFlattenedChildren(
240 FacesContext context,
241 ComponentProcessor<S> childProcessor,
242 Iterable<UIComponent> children,
243 S callbackContext) throws IOException
244 {
245 return processFlattenedChildren(context,
246 new ComponentProcessingContext(),
247 childProcessor,
248 children,
249 callbackContext);
250 }
251
252 /**
253 * Helper function called by Renderers to encode a flattened view of their children,
254 * invoking the <code>childProcessor</code> with its
255 * <code>callbackContext</code> on each renderable instance. This method must be called
256 * when the childProcessor is actually encoding and the childProcessor must not attempt
257 * to encode the same component instances more than once per request.
258 * <p>
259 * If a Renderer needs to
260 * collect information about its possibly flattened children before calling
261 * <code>encodeFlattenedChild(FacesContext, ComponentProcessor, Iterable<UIComponent>, Object)</code>,
262 * it should call
263 * <code>processFlattenedChildren(FacesContext, ComponentProcessor, Iterable<UIComponent>, Object)</code>
264 * to collect the information.
265 * <p>
266 * For each FlattenedComponent child, the <code>childProcessor</code> will
267 * be called on each of that FlattenedComponent's children, recursing if those children are
268 * themselves FlattenedComponents, otherwise, the <code>childProcessor</code> will be called on
269 * the child itself.
270 * <p>
271 * FlattenedComponents that wish to check whether they are processed for the purpose of
272 * encoding can check the ProcessingHints of the ComponentProcessingContext for the
273 * presence of <code>PROCESS_FOR_ENCODING hint</code>.
274 * @param context FacesContext
275 * @param childProcessor ComponentProcessor to call on each flattened child
276 * @param children Initial set of children to flatten
277 * @param callbackContext context object to pass to the childProcessor on each invocation
278 * @return <code>true</code> If any children were processed
279 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, Iterable, Object)
280 * @see FlattenedComponent
281 */
282 public static <S> boolean encodeFlattenedChildren(
283 FacesContext context,
284 ComponentProcessor<S> childProcessor,
285 Iterable<UIComponent> children,
286 S callbackContext) throws IOException
287 {
288 ComponentProcessingContext processingContext = new ComponentProcessingContext();
289 processingContext.__setIsRendering();
290
291 return processFlattenedChildren(context,
292 processingContext,
293 childProcessor,
294 children,
295 callbackContext);
296 }
297
298
299 /**
300 * Helper function called by FlattenedComponents to iterate over a flattened view of their
301 * children, potentially themselves FlattenedComponents, invoking the <code>childProcessor</code>
302 * with its <code>callbackContext</code> on each renderable instance.
303 * <p>
304 * For each FlattenedComponent child, the <code>childProcessor</code> will
305 * be called on each of that FlattenedComponent's children, recursing if those children are
306 * themselves FlattenedComponents, otherwise, the <code>childProcessor</code> will be called on
307 * the child itself.
308 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, UIComponent, Object)
309 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessor, Iterable, Object)
310 * @see UIXComponent#processFlattenedChildren(FacesContext, ComponentProcessingContext, ComponentProcessor, UIComponent, Object)
311 * @see FlattenedComponent
312 */
313 public static <S> boolean processFlattenedChildren(
314 FacesContext context,
315 ComponentProcessingContext cpContext,
316 ComponentProcessor<S> childProcessor,
317 Iterable<UIComponent> children,
318 S callbackContext) throws IOException
319 {
320 // we haven't processed a child yet
321 boolean processedChild = false;
322
323 for (UIComponent currChild : children)
324 {
325 // latch processed child to the first child processed
326 processedChild |= processFlattenedChildren(context,
327 cpContext, childProcessor,
328 currChild,
329 callbackContext);
330 }
331
332 return processedChild;
333 }
334
335 /**
336 * <p>Perform a tree visit starting at
337 * this node in the tree.</p>
338 *
339 * <p>UIXComponent.visitTree() implementations do not invoke the
340 * {@code VisitCallback}directly, but instead call
341 * {@code VisitContext.invokeVisitCallback()}to invoke the
342 * callback. This allows {@code VisitContext}implementations
343 * to provide optimized tree traversals, for example by only
344 * calling the {@code VisitCallback}for a subset of components.</p>
345 *
346 * @param visitContext the <code>VisitContext</code> for this visit
347 * @param callback the <code>VisitCallback</code> instance
348 * whose <code>visit</code> method will be called
349 * for each node visited.
350 * @return component implementations may return <code>true</code>
351 * to indicate that the tree visit is complete (eg. all components
352 * that need to be visited have been visited). This results in
353 * the tree visit being short-circuited such that no more components
354 * are visited.
355 *
356 * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback()
357 */
358 public boolean visitTree(
359 VisitContext visitContext,
360 VisitCallback callback)
361 {
362 return visitTree(visitContext, this, callback);
363 }
364
365 /**
366 * Specifies what facets and children components should be processed as rendered for life-cycle
367 * methods. Any components not returned will not be processed during methods such as decoding,
368 * validating, updating the model, rendered-only tree visiting, etc.
369 *
370 * @param facesContext the facesContext
371 * @return An iterator of components to process. Must not return null (return an empty iterator
372 * if no children components should be processed).
373 */
374 protected Iterator<UIComponent> getRenderedFacetsAndChildren(
375 FacesContext facesContext)
376 {
377 Renderer renderer = getRenderer(facesContext);
378 if (renderer instanceof CoreRenderer)
379 {
380 return ((CoreRenderer)renderer).getRenderedFacetsAndChildren(facesContext, this);
381 }
382 else
383 {
384 return getFacetsAndChildren();
385 }
386 }
387
388 /**
389 * Hook for subclasses to override the manner in which the component's children are visited. The default
390 * implementation visits all of the children and facets of the Component.
391 * <code>setupChildrenVisitingContext</code> will have been called before this method is
392 * invoked and <code>tearDownChildrenVisitingContext</code> will be called after.
393 * respectively. If the purpose of this visit was to encode the component and the
394 * component uses a CoreRenderer, the CoreRenderer's
395 * <code>setupChildrenEncodingContext</code> and <code>tearDownChildrenEncodingContext</code>
396 * will be called before and after this method is invoked, respectively.
397 * @param visitContext the <code>VisitContext</code> for this visit
398 * @param callback the <code>VisitCallback</code> instance
399 * @return <code>true</code> if the visit is complete.
400 * @see #setupChildrenVisitingContext
401 * @see #tearDownChildrenVisitingContext
402 * @see org.apache.myfaces.trinidad.render.CoreRenderer#setupChildrenEncodingContext
403 * @see org.apache.myfaces.trinidad.render.CoreRenderer#tearDownChildrenEncodingContext
404 */
405 protected boolean visitChildren(
406 VisitContext visitContext,
407 VisitCallback callback)
408 {
409 // See if this is during encoding, if so, allow the renderer to control the visitation of
410 // the children so that any special encoding context may be applied around the visitation
411 // of each child.
412 if (_isEncodingVisit(visitContext))
413 {
414 Renderer renderer = getRenderer(visitContext.getFacesContext());
415 if (renderer instanceof CoreRenderer)
416 {
417 CoreRenderer coreRenderer = (CoreRenderer)renderer;
418 return coreRenderer.visitChildrenForEncoding(this, visitContext, callback);
419 }
420 }
421
422 // visit all of the children of the component
423 return visitAllChildren(visitContext, callback);
424 }
425
426 /**
427 * Default implementation of visiting children that visits all children without iterating
428 * @param visitContext the <code>VisitContext</code> for this visit
429 * @param callback the <code>VisitCallback</code> instance
430 * @return <code>true</code> if the visit is complete.
431 */
432 protected final boolean visitAllChildren(
433 VisitContext visitContext,
434 VisitCallback callback)
435 {
436 // visit the children of the component
437 Iterator<UIComponent> kids =
438 visitContext.getHints().contains(VisitHint.SKIP_UNRENDERED) ?
439 getRenderedFacetsAndChildren(visitContext.getFacesContext()) :
440 getFacetsAndChildren();
441
442 while (kids.hasNext())
443 {
444 // If any kid visit returns true, we are done.
445 if (kids.next().visitTree(visitContext, callback))
446 {
447 return true;
448 }
449 }
450
451 return false;
452 }
453
454 /**
455 * Returns <code>true</code> if the components are being visited
456 * for the purpose of encoding.
457 */
458 private static boolean _isEncodingVisit(VisitContext visitContext)
459 {
460 return(visitContext.getHints().contains(VisitHint.EXECUTE_LIFECYCLE) &&
461 FacesContext.getCurrentInstance().getCurrentPhaseId() == PhaseId.RENDER_RESPONSE);
462 }
463
464 /**
465 * <p>Perform a tree visit starting at the specified node in the tree.</p>
466 *
467 * <p>UIXComponent.visitTree() implementations do not invoke the
468 * {@code VisitCallback}directly, but instead call
469 * {@code VisitContext.invokeVisitCallback()}to invoke the
470 * callback. This allows {@code VisitContext}implementations
471 * to provide optimized tree traversals, for example by only
472 * calling the {@code VisitCallback}for a subset of components.</p>
473 *
474 * @param visitContext the <code>VisitContext</code> for this visit
475 * @param component the <code>UIComponent</code> to start the visit from
476 * @param callback the <code>VisitCallback</code> instance
477 * whose <code>visit</code> method will be called
478 * for each node visited.
479 * @return component implementations may return <code>true</code>
480 * to indicate that the tree visit is complete (eg. all components
481 * that need to be visited have been visited). This results in
482 * the tree visit being short-circuited such that no more components
483 * are visited.
484 *
485 * @see VisitContext#invokeVisitCallback VisitContext.invokeVisitCallback()
486 */
487 public static boolean visitTree(
488 VisitContext visitContext,
489 UIComponent component,
490 VisitCallback callback)
491 {
492 if (!(component instanceof UIXComponent))
493 {
494 // hopefully the subview implementations have the subId optimization
495 return component.visitTree(visitContext, callback);
496 }
497 else
498 {
499 UIXComponent uixComponent = (UIXComponent)component;
500
501 FacesContext context = visitContext.getFacesContext();
502
503 // delegate to the UIXComponent
504 if (!uixComponent.isVisitable(visitContext))
505 return false;
506
507 // set up the EL Context with the component. Note that since we do this after call
508 // isVisitable, any attributes retrieved (liek rendered) that are bound with EL referring
509 // to the current component will be evaluated correctly, however, in the specific case
510 // of rendered, rendered already has this problem in normal JSF traversal since it
511 // is evaluated by the parent component
512 component.pushComponentToEL(context, null);
513
514 boolean doneVisiting = false;
515 RuntimeException re = null;
516
517 try
518 {
519 RenderingContext rc = (_isEncodingVisit(visitContext))
520 ? RenderingContext.getCurrentInstance()
521 : null;
522
523 // UIXComponents are allowed to set up their context differently for encoding
524 // than normal processing, so behave differently if this is the RenderResponse
525 // phase. In order to allow the visitcallback to call encodeAll in the ppr case,
526 // we don't call setupEncodingContext before we call the visitContext, since this
527 // would result in setupEncodingContext being called twice on the partial roots,
528 // instead we only do so if the visitCallback returns ACCEPT
529 if (rc == null)
530 {
531 uixComponent.setupVisitingContext(context);
532 }
533
534 VisitResult visitResult = VisitResult.REJECT;
535
536 try
537 {
538 // invoke the callback for this component
539 visitResult = visitContext.invokeVisitCallback(component, callback);
540
541 if (visitResult == VisitResult.COMPLETE)
542 doneVisiting = true;
543 else if (visitResult == VisitResult.ACCEPT)
544 {
545 // now determine whether we need to visit the children
546
547 // assume that all UIXComponent NamingContainers always act as NamingContainers,
548 // (unlike <h:form>) and this it is OK to put the optimization where we
549 // don't visit the children if we know that we don't have any ids in this
550 // subtree to visit
551 boolean skipChildren = (uixComponent instanceof NamingContainer) &&
552 visitContext.getSubtreeIdsToVisit(uixComponent).isEmpty();
553
554 // visit the children of the component if we aren't supposed to skip them
555 if (!skipChildren)
556 {
557 // setup encoding context before visiting children, since we didn't do so
558 // before calling the visitCallback
559 if (rc != null)
560 {
561 uixComponent.setupEncodingContext(context, rc);
562 }
563
564 try
565 {
566 doneVisiting = visitChildren(visitContext, uixComponent, callback);
567 }
568 finally
569 {
570 // teardown the encoding context if we set it up
571 if (rc != null)
572 {
573 uixComponent.tearDownEncodingContext(context, rc);
574 }
575 }
576 }
577 }
578 else
579 {
580 // don't visit the children
581 assert(visitResult == VisitResult.REJECT);
582 }
583 }
584 catch (RuntimeException ex)
585 {
586 re = ex;
587 }
588 finally
589 {
590 try
591 {
592 // tear down the context we set up in order to visit our component
593 if (rc == null)
594 {
595 uixComponent.tearDownVisitingContext(context);
596 }
597 }
598 catch (RuntimeException ex)
599 {
600 if (re == null)
601 {
602 throw ex;
603 }
604 else
605 {
606 _LOG.warning(ex);
607 }
608 }
609 }
610 }
611 finally
612 {
613 component.popComponentFromEL(context);
614
615 if (re != null)
616 {
617 throw re;
618 }
619 }
620
621 // if we got this far, we're not done
622 return doneVisiting;
623 }
624 }
625
626 /**
627 * Utility method to allow the visiting of children components while visiting a parent using
628 * a new visit callback or visit context. The method may only be called when the parent is
629 * is the target of a visitation to ensure that it is properly in context.
630 * <p>Example usage:</p>
631 * <pre>@Override
632 * public VisitResult visit(
633 * VisitContext visitContext,
634 * UIComponent target)
635 * {
636 * if (someCondition)
637 * {
638 * UIXComponent.visitChildren(target, visitContext, new VisitCallback() {...});
639 * return VisitResult.COMPLETE;
640 * }
641 * ...
642 * }</pre>
643 *
644 * @param visitContext the <code>VisitContext</code> for this visit
645 * @param parentComponent the <code>UIComponent</code> to visit the children. The parent component
646 * must be actively being visited in order to call this method.
647 * @param callback the <code>VisitCallback</code> instance
648 * whose <code>visit</code> method will be called
649 * for each node visited.
650 * @return component implementations may return <code>true</code>
651 * to indicate that the tree visit is complete (eg. all components
652 * that need to be visited have been visited). This results in
653 * the tree visit being short-circuited such that no more components
654 * are visited.
655 */
656 public static boolean visitChildren(
657 VisitContext visitContext,
658 UIComponent parentComponent,
659 VisitCallback callback)
660 {
661 if (!(parentComponent instanceof UIXComponent))
662 {
663 // Not a UIXComponent, there is no extra functionality necessary in order to visit the
664 // children.
665 for (Iterator<UIComponent> iter = parentComponent.getFacetsAndChildren(); iter.hasNext();)
666 {
667 UIComponent child = iter.next();
668
669 if (child.visitTree(visitContext, callback))
670 {
671 return true;
672 }
673 }
674
675 return false;
676 }
677
678 UIXComponent uixParentComponent = (UIXComponent)parentComponent;
679 FacesContext context = visitContext.getFacesContext();
680 RenderingContext rc = (_isEncodingVisit(visitContext))
681 ? RenderingContext.getCurrentInstance()
682 : null;
683 boolean doneVisiting = false;
684 RuntimeException re = null;
685
686 // setup any context needed for visiting the children of the component as opposed
687 // to the component itself
688
689 if (parentComponent instanceof UIXComponent)
690 {
691 if (rc != null)
692 {
693 uixParentComponent.setupChildrenEncodingContext(context, rc);
694 }
695 else
696 {
697 uixParentComponent.setupChildrenVisitingContext(context);
698 }
699 }
700
701 try
702 {
703 doneVisiting = uixParentComponent.visitChildren(visitContext, callback);
704 }
705 catch (RuntimeException ex)
706 {
707 re = ex;
708 }
709 finally
710 {
711 try
712 {
713 // teardown any context initialized above
714 if (rc != null)
715 {
716 uixParentComponent.tearDownChildrenEncodingContext(context, rc);
717 }
718 else
719 {
720 uixParentComponent.tearDownChildrenVisitingContext(context);
721 }
722 }
723 catch (RuntimeException ex)
724 {
725 if (re == null)
726 {
727 throw ex;
728 }
729 else
730 {
731 _LOG.warning(ex);
732 }
733 }
734
735 if (re != null)
736 {
737 throw re;
738 }
739 }
740
741 return doneVisiting;
742 }
743
744 /**
745 * Add a component as a partial target to the current request. This code handles the
746 * delegation to {@link #setPartialTarget(FacesContext, PartialPageContext)}
747 * for UIXComponents or assumes for {@link UIComponent} that components with a renderer
748 * type are able to produce DOM elements that have IDs that can be replaced.
749 *
750 * @param facesContext the faces context
751 * @param partialContext the partial page context
752 * @param component the component to add as a target
753 */
754 public static void addPartialTarget(FacesContext facesContext,
755 PartialPageContext partialContext, UIComponent component)
756 {
757 if(component == null)
758 {
759 throw new NullPointerException("UIComponent is null");
760 }
761
762 if (component instanceof UIXComponent)
763 {
764 ((UIXComponent)component).setPartialTarget(facesContext, partialContext);
765 }
766 else
767 {
768 // default to using the renderer type implementation
769 _addPartialTargetImpl(facesContext, partialContext, component);
770 }
771 }
772
773 /**
774 * Clears all of the cached clientIds in this component subtree
775 */
776 public final void clearCachedClientIds()
777 {
778 clearCachedClientIds(this);
779 }
780
781 /**
782 * Clears all of the cached clientIds in the component's subtree
783 * @param component Component subtree to clear the cached client ids for
784 */
785 public static void clearCachedClientIds(UIComponent component)
786 {
787 String id = component.getId();
788 component.setId(id);
789
790 Iterator<UIComponent> allChildren = component.getFacetsAndChildren();
791
792 while (allChildren.hasNext())
793 clearCachedClientIds(allChildren.next());
794 }
795
796 /**
797 * Marks this component as a partial target for this request. Typically called
798 * by the {@link org.apache.myfaces.trinidad.context.RequestContext RequestContext}.
799 * The component should add the client ID the desired rendered component to the context.
800 * This allows components that do not render a replacable DOM element with an ID
801 * to choose an alternative component, like a parent.
802 *
803 * @param facesContext the faces context
804 * @param partialContext the partial page context
805 */
806 protected void setPartialTarget(FacesContext facesContext,
807 PartialPageContext partialContext)
808 {
809 UIXComponent._addPartialTargetImpl(facesContext, partialContext, this);
810 }
811
812 /**
813 * <p>Called by
814 * {@link UIXComponent#visitTree(VisitContext,VisitCallback) UIXComponent.visitTree()}to determine
815 * whether this component is "visitable" - ie. whether this component
816 * satisfies the {@link VisitHint}returned by
817 * {@link VisitContext#getHints VisitContext.getHints()}.</p>
818 * <p>If this component is not visitable (ie. if this method returns
819 * false), the tree visited is short-circuited such that neither the
820 * component nor any of its descendents will be visited></p>
821 * <p>Custom {@code treeVisit()}implementations may call this method
822 * to determine whether the component is visitable before performing
823 * any visit-related processing.</p>
824 * @param visitContext VisitingContext to use to determine if the component is visitable
825 * @return true if this component should be visited, false otherwise.
826 */
827 protected boolean isVisitable(VisitContext visitContext)
828 {
829 return _isVisitable(visitContext, this);
830 }
831
832 /**
833 * @see #addPartialTarget(FacesContext, PartialPageContext, UIComponent)
834 * @see #setPartialTarget(FacesContext, PartialPageContext)
835 */
836 private static void _addPartialTargetImpl(
837 FacesContext facesContext, PartialPageContext partialContext, UIComponent component)
838 {
839 if (component.getRendererType() == null)
840 {
841 if (component.getParent() != null)
842 {
843 // delegate to the parent component, assuming that no renderer type means that
844 // there is no suitable replacable DOM element for this component
845 addPartialTarget(facesContext, partialContext, component.getParent());
846 }
847 }
848 else
849 {
850 partialContext.addPartialTarget(component.getClientId(facesContext));
851 }
852 }
853
854 /**
855 * default implementation checking the <code>VisitHint.SKIP_TRANSIENT</code> and
856 * <code>VisitHint.SKIP_UNRENDERED</code> hints.
857 */
858 private static boolean _isVisitable(VisitContext visitContext, UIComponent component)
859 {
860 Collection<VisitHint> hints = visitContext.getHints();
861
862 if (hints.contains(VisitHint.SKIP_TRANSIENT) && component.isTransient())
863 return false;
864
865 if (hints.contains(VisitHint.SKIP_UNRENDERED) && !component.isRendered())
866 return false;
867
868 return true;
869 }
870
871 /**
872 * <p>
873 * Called when visiting the component during optimized partial page encoding so that the
874 * component can modify what is actually encoded. For example tab controls often
875 * render the tabs for the ShowDetailItems in the tab bar before delegating to the
876 * disclosed ShowDetailItem to render the tab content. As a result, the tab control
877 * needs to encode its tab bar if any of its ShowDetailItems are partial targets so that
878 * the tab labels, for example, are up-to-date.
879 * </p>
880 * <p>
881 * The default implementation delegates to the CoreRenderer if this component has one, otherwise
882 * it calls the VisitCallback and returns its result if this UIXComponent is a partial
883 * target of the current encoding.
884 * </p>
885 * @param visitContext VisitContext to pass to the VisitCallback
886 * @param partialContext PartialPageContext for the current partial encoding
887 * @param callback VisitCallback to call if this component is a partial target
888 * @return The VisitResult controlling continued iteration of the visit.
889 */
890 public VisitResult partialEncodeVisit(
891 VisitContext visitContext,
892 PartialPageContext partialContext,
893 VisitCallback callback)
894 {
895 FacesContext context = visitContext.getFacesContext();
896 Renderer renderer = getRenderer(context);
897
898 if (renderer instanceof CoreRenderer)
899 {
900 // delegate to the CoreRenderer
901 return ((CoreRenderer)renderer).partialEncodeVisit(visitContext,
902 partialContext,
903 this,
904 callback);
905 }
906 else
907 {
908 // check that this is a component instance that should be encoded
909 if (partialContext.isPossiblePartialTarget(getId()) &&
910 partialContext.isPartialTarget(getClientId(context)))
911 {
912 // visit the component instance
913 return callback.visit(visitContext, this);
914 }
915 else
916 {
917 // Not visiting this component, but allow visit to
918 // continue into this subtree in case we've got
919 // visit targets there.
920 return VisitResult.ACCEPT;
921 }
922 }
923 }
924
925 /**
926 * <p>Sets up the context necessary to visit or invoke the component for all phases.</p>
927 * <p>The default implementation does nothing.</p>
928 * <p>If a subclass overrides this method, it should override
929 * <code>tearDownVisitingContext</code> as well.</p>
930 * <p>It is guaranteed that if <code>setupVisitingContext</code> completes
931 * <code>tearDownVisitingContext</code> will be called for this component</p>
932 * @param context FacesContext
933 * @see #tearDownVisitingContext
934 * @see #setupEncodingContext
935 * @see #tearDownEncodingContext
936 * @see #setupChildrenVistingContext
937 */
938 protected void setupVisitingContext(@SuppressWarnings("unused") FacesContext context)
939 {
940 // If in testing phase, add code to determine if this function is being used correctly.
941 if (_inTestingPhase)
942 {
943 // First, check to ensure that we are not already in context
944 if (_inVisitingContext)
945 {
946 _handleInvalidContextUsage("COMPONENT_ALREADY_IN_VISITING_CONTEXT", _setupVisitingCaller);
947 }
948
949 // Next, add a context change so that the flags are reset during an
950 // invokeOnComponent(context, clientId, callback), or a visitTree call:
951 ComponentContextManager componentContextManager =
952 RequestContext.getCurrentInstance().getComponentContextManager();
953 componentContextManager.pushChange(new VisitDebugContextChange(this));
954 _inVisitingContext = true;
955 _setupVisitingCaller = _getStackTraceElementForCaller();
956 }
957 }
958
959 /**
960 * <p>Sets up the context necessary to visit or invoke the children of a component for all phases.
961 * </p>
962 * <p>The default implementation does nothing.</p>
963 * <p>If a subclass overrides this method, it should override
964 * <code>tearDownChildrenVisitingContext</code> as well.</p>
965 * <p>It is guaranteed that if <code>setupChildrenVisitingContext</code> completes
966 * <code>tearDownChildrenVisitingContext</code> will be called for this component</p>
967 * @param context FacesContext
968 * @see #visitChildren
969 * @see #tearDownChildrenVisitingContext
970 * @see setupVisitingContext
971 */
972 protected void setupChildrenVisitingContext(@SuppressWarnings("unused") FacesContext context)
973 {
974 // If in testing phase, add code to determine if this function is being used correctly.
975 if (_inTestingPhase)
976 {
977 // Check to ensure that we are not already in context
978 if (_inChildrenVisitingContext)
979 {
980 _handleInvalidContextUsage("COMPONENT_ALREADY_IN_CHILDREN_VISITING_CONTEXT",
981 _setupChildrenVisitingCaller);
982 }
983
984 // Next, add a context change so that the flags are reset during an
985 // invokeOnComponent(context, clientId, callback), or a visitTree call
986 // (Note that a separate one is needed for the children due to the fact that currently
987 // the encoding context / visiting context is not set up during normal tree traversal,
988 // but the children encoding context is setup by the renderers):
989 ComponentContextManager componentContextManager =
990 RequestContext.getCurrentInstance().getComponentContextManager();
991 componentContextManager.pushChange(new VisitChildrenDebugContextChange(this));
992 _inChildrenVisitingContext = true;
993 _setupChildrenVisitingCaller = _getStackTraceElementForCaller();
994 }
995 }
996
997 /**
998 * <p>Tears down context created in order to visit or invoke the component
999 * for all phases.</p>
1000 * <p>The default implementation does nothing.</p>
1001 * <p>A subclass should only override this method if it overrode
1002 * <code>setupVisitingContext</code> as well</p>
1003 * <p>It is guaranteed that <code>tearDownVisitingContext</code> will be called only after
1004 * <code>setupVisitingContext</code> has been called for this component</p>
1005 * @param context FacesContext
1006 * @see #setupVisitingContext
1007 * @see #setupEncodingContext
1008 * @see #tearDownEncodingContext
1009 */
1010 protected void tearDownVisitingContext(@SuppressWarnings("unused") FacesContext context)
1011 {
1012 // If in testing phase, add code to determine if this function is being used correctly.
1013 if (_inTestingPhase)
1014 {
1015 // First, check to ensure that we are not already in context
1016 if (!_inVisitingContext)
1017 {
1018 _handleInvalidContextUsage("COMPONENT_NOT_IN_VISITING_CONTEXT", _tearDownVisitingCaller);
1019 }
1020
1021 // Next, remove the context change that was added in setupVisitingContext:
1022 ComponentContextManager componentContextManager =
1023 RequestContext.getCurrentInstance().getComponentContextManager();
1024
1025 ComponentContextChange contextChange = componentContextManager.popChange();
1026
1027 // Validate the state of the context change stack:
1028 if (!(contextChange instanceof VisitDebugContextChange) ||
1029 ((VisitDebugContextChange)contextChange)._component != this)
1030 {
1031 throw new IllegalStateException(_getInvalidContextChangeMessage(
1032 VisitDebugContextChange.class,
1033 contextChange));
1034 }
1035
1036 _inVisitingContext = false;
1037 _tearDownVisitingCaller = _getStackTraceElementForCaller();
1038 }
1039 }
1040
1041 /**
1042 * <p>Tears down context created in order to visit or invoke the children of a component
1043 * for all phases.</p>
1044 * <p>The default implementation does nothing.</p>
1045 * <p>A subclass should only override this method if it overrode
1046 * <code>setupChildrenVisitingContext</code> as well</p>
1047 * <p>It is guaranteed that <code>tearDownChildrenVisitingContext</code> will be called only after
1048 * <code>setupChildrenVisitingContext</code> has been called for this component</p>
1049 * @param context FacesContext
1050 * @see #setupChildrenVisitingContext
1051 * @see #visitChildren
1052 */
1053 protected void tearDownChildrenVisitingContext(@SuppressWarnings("unused") FacesContext context)
1054 {
1055 // If in testing phase, add code to determine if this function is being used correctly.
1056 if (_inTestingPhase)
1057 {
1058 // Check to ensure that we are not already in context
1059 if (!_inChildrenVisitingContext)
1060 {
1061 _handleInvalidContextUsage("COMPONENT_NOT_IN_CHILDREN_VISITING_CONTEXT",
1062 _tearDownChildrenVisitingCaller);
1063 }
1064
1065 // Next, remove the context change that was added in setupChildrenVisitingContext:
1066 ComponentContextManager componentContextManager =
1067 RequestContext.getCurrentInstance().getComponentContextManager();
1068
1069 ComponentContextChange contextChange = componentContextManager.popChange();
1070 // Validate the state of the context change stack:
1071 if (!(contextChange instanceof VisitChildrenDebugContextChange) ||
1072 ((VisitChildrenDebugContextChange)contextChange)._component != this)
1073 {
1074 throw new IllegalStateException(_getInvalidContextChangeMessage(
1075 VisitChildrenDebugContextChange.class,
1076 contextChange));
1077 }
1078
1079 _inChildrenVisitingContext = false;
1080 _tearDownChildrenVisitingCaller = _getStackTraceElementForCaller();
1081 }
1082 }
1083
1084 @Deprecated
1085 protected final void setUpEncodingContext(FacesContext context, RenderingContext rc)
1086 {
1087 setupEncodingContext(context, rc);
1088 }
1089
1090 /**
1091 * <p>Sets up the context necessary to encode the component.</p>
1092 * <p>The default implementation delegates to
1093 * <code>CoreRenderer.setupEncodingContext</code> and then calls
1094 * <code>setupVisitingContext</code></p>
1095 * <p>If a subclass overrides this method, it should override
1096 * <code>tearDownEncodingContext</code> as well.</p>
1097 * <p>It is guaranteed that if <code>setUpEncodingContext</code> completes
1098 * <code>tearDownEncodingContext</code> will be called for this component</p>
1099 * <p>
1100 * During partial page rendering traversals, <code>setupEncodingContext</code> is not called
1101 * before the <code>VisitCallback</code> is invoked. This behavior is different than for
1102 * <code>setupVisitingContext</code>, which is always called before the <code>VisitCallback</code>
1103 * is invoked for non-partial page rendering visits. This difference in behavior allows the
1104 * <code>VisitCallback</code> in a partial page rendering visit to safely call
1105 * <code>UIComponent.encodeAll</code>, which in the case of a UIXComponent, will call
1106 * <code>UIXComponent.setupEncodeContext</code>.
1107 * </p>
1108 * @param context The FacesContext
1109 * @param rc RenderingContext to use for encoding
1110 * @see #setupVisitingContext
1111 * @see #setupChildrenEncodingContext
1112 * @see #tearDownVisitingContext
1113 * @see #tearDownEncodingContext
1114 * @see org.apache.myfaces.trinidad.render.CoreRenderer#setupEncodingContext(FacesContext, RenderingContext, UIComponent)
1115 */
1116 protected void setupEncodingContext(FacesContext context, RenderingContext rc)
1117 {
1118 setupVisitingContext(context);
1119
1120 // If in testing phase, add code to determine if this function is being used correctly.
1121 if (_inTestingPhase)
1122 {
1123 // Check to ensure that we are not already in context
1124 if (_inEncodingContext)
1125 {
1126 _handleInvalidContextUsage("COMPONENT_ALREADY_IN_ENCODING_CONTEXT", _setupEncodingCaller);
1127 }
1128
1129 _inEncodingContext = true;
1130 _setupEncodingCaller = _getStackTraceElementForCaller();
1131 }
1132
1133 Renderer renderer = getRenderer(context);
1134
1135 if (renderer instanceof CoreRenderer)
1136 {
1137 CoreRenderer coreRenderer = (CoreRenderer)renderer;
1138
1139 coreRenderer.setupEncodingContext(context, rc, (UIComponent)this);
1140 }
1141
1142 _inEncodingContext = true;
1143 }
1144
1145 /**
1146 * Sets the context necessary to encode the children of a component.
1147 * @param context The FacesContext
1148 * @param rc RenderingContext to use for encoding
1149 * @see #setupChildrenVisitingContext
1150 * @see #tearDownChildrenEncodingContext
1151 * @see #setupEncodingContext
1152 * @see org.apache.myfaces.trinidad.render.CoreRenderer#setupChildrenEncodingContext
1153 */
1154 public void setupChildrenEncodingContext(FacesContext context, RenderingContext rc)
1155 {
1156 setupChildrenVisitingContext(context);
1157
1158 // If in testing phase, add code to determine if this function is being used correctly.
1159 if (_inTestingPhase)
1160 {
1161 // Check to ensure that we are not already in context
1162 if (_inChildrenEncodingContext)
1163 {
1164 _handleInvalidContextUsage("COMPONENT_ALREADY_IN_CHILDREN_ENCODING_CONTEXT",
1165 _setupChildrenEncodingCaller);
1166 }
1167
1168 _inChildrenEncodingContext = true;
1169 _setupChildrenEncodingCaller = _getStackTraceElementForCaller();
1170 }
1171
1172 Renderer renderer = getRenderer(context);
1173
1174 if (renderer instanceof CoreRenderer)
1175 {
1176 CoreRenderer coreRenderer = (CoreRenderer)renderer;
1177
1178 coreRenderer.setupChildrenEncodingContext(context, rc, this);
1179 }
1180 _inChildrenEncodingContext = true;
1181 }
1182
1183 /**
1184 * <p>Tears down the context created in order to encode the component</p>
1185 * <p>The default implementation delegates to
1186 * <code>CoreRenderer.tearDownEncodingContext</code> and then calls
1187 * <code>tearDownVisitingContext</code></p>
1188 * <p>A subclass should only override this method if it overrode
1189 * <code>setupEncodingContext</code> as well</p>
1190 * <p>It is guaranteed that <code>tearDownEncodingContext</code> will be called only after
1191 * <code>setupEncodingContext</code> has been called for this component</p>
1192 * @param context The FacesContext
1193 * @param rc RenderingContext to use for encoding
1194 * @see #setupEncodingContext
1195 * @see #tearDownVisitingContext
1196 * @see #setupEncodingContext
1197 * @see #tearDownChildrenEncodingContext
1198 * @see org.apache.myfaces.trinidad.render.CoreRenderer#tearDownEncodingContext(FacesContext, RenderingContext, UIComponent)
1199 */
1200 protected void tearDownEncodingContext(
1201 FacesContext context,
1202 RenderingContext rc)
1203 {
1204 // If in testing phase, add code to determine if this function is being used correctly.
1205 if (_inTestingPhase)
1206 {
1207 // Check to ensure that we are not already in context
1208 if (!_inEncodingContext)
1209 {
1210 _handleInvalidContextUsage("COMPONENT_NOT_IN_ENCODING_CONTEXT", _tearDownEncodingCaller);
1211 }
1212
1213 _inEncodingContext = false;
1214 _tearDownEncodingCaller = _getStackTraceElementForCaller();
1215 }
1216
1217 Renderer renderer = getRenderer(context);
1218
1219 try
1220 {
1221 if (renderer instanceof CoreRenderer)
1222 {
1223 CoreRenderer coreRenderer = (CoreRenderer)renderer;
1224
1225 coreRenderer.tearDownEncodingContext(context, rc, (UIComponent)this);
1226 }
1227 }
1228 finally
1229 {
1230 tearDownVisitingContext(context);
1231 }
1232 }
1233
1234 /**
1235 * Tears down the context necessary to encode the children of a component.
1236 * @param context The FacesContext
1237 * @param rc RenderingContext to use for encoding
1238 * @see #setupChildrenVisitingContext
1239 * @see #tearDownChildrenEncodingContext
1240 * @see #tearDownEncodingContext
1241 * @see org.apache.myfaces.trinidad.render.CoreRenderer#setupChildrenEncodingContext
1242 */
1243 public void tearDownChildrenEncodingContext(
1244 FacesContext context,
1245 RenderingContext rc)
1246 {
1247 // If in testing phase, add code to determine if this function is being used correctly.
1248 if (_inTestingPhase)
1249 {
1250 // Check to ensure that we are not already in context
1251 if (!_inChildrenEncodingContext)
1252 {
1253 _handleInvalidContextUsage("COMPONENT_NOT_IN_CHILDREN_ENCODING_CONTEXT",
1254 _tearDownChildrenEncodingCaller);
1255 }
1256
1257 _inChildrenEncodingContext = false;
1258 _tearDownChildrenEncodingCaller = _getStackTraceElementForCaller();
1259 }
1260
1261 Renderer renderer = getRenderer(context);
1262
1263 try
1264 {
1265 if (renderer instanceof CoreRenderer)
1266 {
1267 CoreRenderer coreRenderer = (CoreRenderer)renderer;
1268
1269 coreRenderer.tearDownChildrenEncodingContext(context, rc, this);
1270 }
1271 }
1272 finally
1273 {
1274 tearDownChildrenVisitingContext(context);
1275 _inChildrenEncodingContext = false;
1276 }
1277 }
1278
1279 /**
1280 * Returns the FacesBean used for storing the component's state.
1281 */
1282 abstract public FacesBean getFacesBean();
1283
1284 /**
1285 * Adds an AttributeChangeListener. Attribute change events are not
1286 * delivered for any programmatic change to a property. They are only
1287 * delivered when a renderer changes a property without the application's
1288 * specific request. An example of an attribute change events might
1289 * include the width of a column that supported client-side resizing.
1290 */
1291 abstract public void addAttributeChangeListener(AttributeChangeListener acl);
1292
1293 /**
1294 * Removes an AttributeChangeListener. Attribute change events are not
1295 * delivered for any programmatic change to a property. They are only
1296 * delivered when a renderer changes a property without the application's
1297 * specific request. An example of an attribute change events might
1298 * include the width of a column that supported client-side resizing.
1299 */
1300 abstract public void removeAttributeChangeListener(AttributeChangeListener acl);
1301
1302 /**
1303 * Gets the registered AttributeChangeListeners.
1304 */
1305 abstract public AttributeChangeListener[] getAttributeChangeListeners();
1306
1307 /**
1308 * Sets a method binding to an AttributeChangeListener. Attribute
1309 * change events are not
1310 * delivered for any programmatic change to a property. They are only
1311 * delivered when a renderer changes a property without the application's
1312 * specific request. An example of an attribute change events might
1313 * include the width of a column that supported client-side resizing.
1314 */
1315 abstract public void setAttributeChangeListener(MethodExpression me);
1316
1317 /**
1318 * Gets the method binding to an AttributeChangeListener. Attribute
1319 * change events are not
1320 * delivered for any programmatic change to a property. They are only
1321 * delivered when a renderer changes a property without the application's
1322 * specific request. An example of an attribute change events might
1323 * include the width of a column that supported client-side resizing.
1324 */
1325 abstract public MethodExpression getAttributeChangeListener();
1326
1327 abstract public void markInitialState();
1328
1329 /**
1330 * Provides additional context (the target child component for which the container
1331 * client ID is requested) to a naming container for constructing a client ID.
1332 * This is useful for components such as @link UIXTable and @link UIXTreeTable which need
1333 * to return different container client IDs for stamped and non-stamped child components.
1334 * @see UIXComponentBase#getClientId(FacesContext context)
1335 */
1336 abstract public String getContainerClientId(FacesContext context, UIComponent child);
1337
1338
1339 /**
1340 * Provides a logical parent for this component (a parent in the context of the document where this component was
1341 * defined). The default implementation will simply call getParent(). Components that get relocated during
1342 * tag execution will return their original parent
1343 * @return logical parent component
1344 */
1345 public UIComponent getLogicalParent()
1346 {
1347 return getParent();
1348 }
1349
1350 /**
1351 * Provides a logical parent for the component (a parent in the context of the document where the component was
1352 * defined). The default implementation will simply call getParent() on the component. Components that get relocated during
1353 * tag execution should have their original parent returned (if available).
1354 * @param component - child component whose parent is being retrieved
1355 * @return logical parent component
1356 */
1357 public static UIComponent getLogicalParent(UIComponent component)
1358 {
1359 if (component instanceof UIXComponent)
1360 {
1361 return ((UIXComponent)component).getLogicalParent();
1362 }
1363
1364 return component.getParent();
1365 }
1366
1367 /**
1368 * We are using FacesBean to save state, which does not implement StateHelper, so
1369 * calling this method will call UnsupportedOperationException
1370 */
1371 @Override
1372 protected StateHelper getStateHelper()
1373 {
1374 throw new UnsupportedOperationException();
1375 }
1376
1377 /**
1378 * We are using FacesBean to save state, which does not implement StateHelper, so
1379 * calling this method will call UnsupportedOperationException
1380 */
1381 @Override
1382 protected StateHelper getStateHelper(boolean create)
1383 {
1384 throw new UnsupportedOperationException();
1385 }
1386
1387 private String _getStackTraceElementForCaller()
1388 {
1389 StackTraceElement[] elements = Thread.currentThread().getStackTrace();
1390
1391 // 0 is the call to getStackTrace, 1 is this method, 2 is the calling method in UIXComponent
1392 // so 3 (4th element) is the caller to the UIXComponent method, the one that is desired.
1393
1394 String lineSep = System.getProperty("line.separator");
1395 StringBuilder stack = new StringBuilder();
1396 for (int i = 3, size = elements.length; i < size && i < 10; ++i)
1397 {
1398 stack.append(elements[i].toString());
1399 stack.append(lineSep);
1400 }
1401
1402 return stack.toString();
1403 }
1404
1405 /**
1406 * Determine if we can flatten a core JSF component.
1407 * @param component The component
1408 * @return true if the component is a core JSF component and we can
1409 * flatten it successfully.
1410 */
1411 private static boolean isFlattenableCoreComponent(UIComponent component)
1412 {
1413 // Optimize the cases of UINamingContainer (<f:subview>) and UIPanel -
1414 // we will treat these components as FlattenedComponents because they do not render
1415 // any DOM.
1416 // Also note that as of JSF 2.0, UINamingContainer components are built
1417 // by f:subview, as well as composite components.
1418 Class<? extends UIComponent> componentClass = component.getClass();
1419
1420 if (UINamingContainer.class == componentClass)
1421 {
1422 // Check to see if this component was created as a composite
1423 // component, which we cannot flatten
1424 return component.getFacet(UIComponent.COMPOSITE_FACET_NAME) == null;
1425 }
1426
1427 // Note that JSF 2.0 creates UIPanel wrappers around multiple components
1428 // inside of <f:facet>
1429 return UIPanel.class == componentClass;
1430 }
1431
1432 private void _handleInvalidContextUsage(
1433 String bundleKey,
1434 String originalStackTrace
1435 ) throws IllegalStateException
1436 {
1437 String errorMessage = _LOG.getMessage(bundleKey,
1438 new Object[] { getClientId(), originalStackTrace });
1439
1440 if (_treatContextualIssuesAsErrors)
1441 {
1442 throw new IllegalStateException(errorMessage);
1443 }
1444 else
1445 {
1446 _LOG.warning(errorMessage);
1447 }
1448 }
1449
1450 private String _getInvalidContextChangeMessage(
1451 Class<? extends ComponentContextChange> expectedClass,
1452 ComponentContextChange foundChange)
1453 {
1454 String type = expectedClass.getName();
1455 String id = (getParent() == null) ? getId() : getClientId();
1456
1457 return _LOG.getMessage("INVALID_CONTEXT_CHANGE_FOUND",
1458 new Object[] { type, id, foundChange });
1459 }
1460
1461 private static class VisitDebugContextChange
1462 extends ComponentContextChange
1463 {
1464 private VisitDebugContextChange(
1465 UIXComponent component)
1466 {
1467 _component = component;
1468 }
1469
1470 @Override
1471 public void resume(FacesContext facesContext)
1472 {
1473 _component._inVisitingContext = _inVisitingContext;
1474 _component._inEncodingContext = _inEncodingContext;
1475 _component._setupVisitingCaller = _setupVisitingCaller;
1476 _component._tearDownVisitingCaller = _tearDownVisitingCaller;
1477 _component._setupEncodingCaller = _setupEncodingCaller;
1478 _component._tearDownEncodingCaller = _tearDownEncodingCaller;
1479 }
1480
1481 @Override
1482 public void suspend(FacesContext facesContext)
1483 {
1484 _inVisitingContext = _component._inVisitingContext;
1485 _inEncodingContext = _component._inEncodingContext;
1486 _setupVisitingCaller = _component._setupVisitingCaller;
1487 _tearDownVisitingCaller = _component._tearDownVisitingCaller;
1488 _setupEncodingCaller = _component._setupEncodingCaller;
1489 _tearDownEncodingCaller = _component._tearDownEncodingCaller;
1490
1491 _component._inVisitingContext = false;
1492 _component._inEncodingContext = false;
1493 _component._setupVisitingCaller = null;
1494 _component._tearDownVisitingCaller = null;
1495 _component._setupEncodingCaller = null;
1496 _component._tearDownEncodingCaller = null;
1497 }
1498
1499 @Override
1500 public String toString()
1501 {
1502 return String.format("VisitDebugContextChange(component: %s, id: %s)",
1503 _component,
1504 _component == null ? null :
1505 _component.getParent() == null ? _component.getId() : _component.getClientId());
1506 }
1507
1508 private final UIXComponent _component;
1509 private boolean _inVisitingContext;
1510 private boolean _inEncodingContext;
1511 private String _setupVisitingCaller;
1512 private String _tearDownVisitingCaller;
1513 private String _setupEncodingCaller;
1514 private String _tearDownEncodingCaller;
1515 }
1516
1517 private static class VisitChildrenDebugContextChange
1518 extends ComponentContextChange
1519 {
1520 private VisitChildrenDebugContextChange(
1521 UIXComponent component)
1522 {
1523 _component = component;
1524 }
1525
1526 @Override
1527 public void resume(FacesContext facesContext)
1528 {
1529 _component._inChildrenVisitingContext = _inChildrenVisitingContext;
1530 _component._inChildrenEncodingContext = _inChildrenEncodingContext;
1531 _component._setupChildrenEncodingCaller = _setupChildrenEncodingCaller;
1532 _component._tearDownChildrenEncodingCaller = _tearDownChildrenEncodingCaller;
1533 _component._setupChildrenVisitingCaller = _setupChildrenVisitingCaller;
1534 _component._tearDownChildrenVisitingCaller = _tearDownChildrenVisitingCaller;
1535 }
1536
1537 @Override
1538 public void suspend(FacesContext facesContext)
1539 {
1540 _inChildrenVisitingContext = _component._inChildrenVisitingContext;
1541 _inChildrenEncodingContext = _component._inChildrenEncodingContext;
1542 _setupChildrenEncodingCaller = _component._setupChildrenEncodingCaller;
1543 _tearDownChildrenEncodingCaller = _component._tearDownChildrenEncodingCaller;
1544 _setupChildrenVisitingCaller = _component._setupChildrenVisitingCaller;
1545 _tearDownChildrenVisitingCaller = _component._tearDownChildrenVisitingCaller;
1546
1547 _component._inChildrenVisitingContext = false;
1548 _component._inChildrenEncodingContext = false;
1549 _component._setupChildrenEncodingCaller = null;
1550 _component._tearDownChildrenEncodingCaller = null;
1551 _component._setupChildrenVisitingCaller = null;
1552 _component._tearDownChildrenVisitingCaller = null;
1553 }
1554
1555 @Override
1556 public String toString()
1557 {
1558 return String.format("VisitChildrenDebugContextChange(component: %s, id: %s)",
1559 _component,
1560 _component == null ? null :
1561 _component.getParent() == null ? _component.getId() : _component.getClientId());
1562 }
1563
1564 private final UIXComponent _component;
1565 private boolean _inChildrenVisitingContext;
1566 private boolean _inChildrenEncodingContext;
1567 private String _setupChildrenEncodingCaller;
1568 private String _tearDownChildrenEncodingCaller;
1569 private String _setupChildrenVisitingCaller;
1570 private String _tearDownChildrenVisitingCaller;
1571 }
1572
1573 // Use logging for now until all issues are resolved
1574 private final static boolean _treatContextualIssuesAsErrors;
1575 private final static boolean _inTestingPhase;
1576 private static final TrinidadLogger _LOG =
1577 TrinidadLogger.createTrinidadLogger(UIXComponent.class);
1578
1579 static
1580 {
1581 // If Trinidad is to be ever shared with a shared class loader, we should change this
1582 // static boolean flag to be non-class loader bound.
1583 Application app = null;
1584 try
1585 {
1586 FacesContext facesContext = FacesContext.getCurrentInstance();
1587 app = facesContext == null ? null : facesContext.getApplication();
1588 if (app == null)
1589 {
1590 ApplicationFactory appFactory = (ApplicationFactory)
1591 FactoryFinder.getFactory(FactoryFinder.APPLICATION_FACTORY);
1592 app = appFactory.getApplication();
1593 }
1594 }
1595 catch (Throwable t)
1596 {
1597 // This occurs during unit testing without a full JSF environment, ignore
1598 ;
1599 }
1600
1601 _inTestingPhase = app == null ? false : app.getProjectStage() == ProjectStage.UnitTest;
1602 if (_inTestingPhase)
1603 {
1604 _LOG.info("Application is running in testing phase, UIXComponent will " +
1605 "perform extra validation steps to ensure proper component usage");
1606 }
1607
1608 _treatContextualIssuesAsErrors = "true".equals(
1609 System.getProperty("uixcomponent.contextual.issue.throw"));
1610 };
1611
1612 private String _setupVisitingCaller;
1613 private String _tearDownVisitingCaller;
1614 private String _setupEncodingCaller;
1615 private String _tearDownEncodingCaller;
1616 private String _setupChildrenEncodingCaller;
1617 private String _tearDownChildrenEncodingCaller;
1618 private String _setupChildrenVisitingCaller;
1619 private String _tearDownChildrenVisitingCaller;
1620 private boolean _inVisitingContext;
1621 private boolean _inChildrenVisitingContext;
1622 private boolean _inEncodingContext;
1623 private boolean _inChildrenEncodingContext;
1624 }