View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.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&lt;UIComponent&gt;, Object)</code>,
262    * it should call
263    * <code>processFlattenedChildren(FacesContext, ComponentProcessor, Iterable&lt;UIComponent&gt;, 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 }