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