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.ByteArrayOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.ObjectOutputStream;
25  
26  import java.net.URL;
27  
28  import java.util.ArrayList;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Properties;
35  
36  import javax.el.ELContext;
37  import javax.el.ELException;
38  import javax.el.MethodExpression;
39  import javax.el.ValueExpression;
40  
41  import javax.faces.FacesException;
42  import javax.faces.application.ProjectStage;
43  import javax.faces.application.Resource;
44  import javax.faces.component.ContextCallback;
45  import javax.faces.component.NamingContainer;
46  import javax.faces.component.StateHolder;
47  import javax.faces.component.UIComponent;
48  import javax.faces.component.UIViewRoot;
49  import javax.faces.component.behavior.ClientBehavior;
50  import javax.faces.component.behavior.ClientBehaviorHolder;
51  import javax.faces.context.ExternalContext;
52  import javax.faces.context.FacesContext;
53  import javax.faces.el.EvaluationException;
54  import javax.faces.el.MethodBinding;
55  import javax.faces.el.ValueBinding;
56  import javax.faces.event.AbortProcessingException;
57  import javax.faces.event.ComponentSystemEvent;
58  import javax.faces.event.ComponentSystemEventListener;
59  import javax.faces.event.FacesEvent;
60  import javax.faces.event.FacesListener;
61  import javax.faces.event.PostAddToViewEvent;
62  import javax.faces.event.PreRemoveFromViewEvent;
63  import javax.faces.event.PreRenderComponentEvent;
64  import javax.faces.event.SystemEvent;
65  import javax.faces.event.SystemEventListener;
66  import javax.faces.render.RenderKit;
67  import javax.faces.render.Renderer;
68  
69  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
70  import org.apache.myfaces.trinidad.bean.AttachedObjects;
71  import org.apache.myfaces.trinidad.bean.FacesBean;
72  import org.apache.myfaces.trinidad.bean.FacesBeanFactory;
73  import org.apache.myfaces.trinidad.bean.PropertyKey;
74  import org.apache.myfaces.trinidad.bean.util.StateUtils;
75  import org.apache.myfaces.trinidad.bean.util.ValueMap;
76  import org.apache.myfaces.trinidad.change.AttributeComponentChange;
77  import org.apache.myfaces.trinidad.change.ComponentChange;
78  import org.apache.myfaces.trinidad.change.ComponentChangeFilter;
79  import org.apache.myfaces.trinidad.change.RowKeySetAttributeChange;
80  import org.apache.myfaces.trinidad.component.ComponentProcessingContext.ProcessingHint;
81  import org.apache.myfaces.trinidad.context.RenderingContext;
82  import org.apache.myfaces.trinidad.context.RequestContext;
83  import org.apache.myfaces.trinidad.event.AttributeChangeEvent;
84  import org.apache.myfaces.trinidad.event.AttributeChangeListener;
85  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
86  import org.apache.myfaces.trinidad.model.RowKeySet;
87  import org.apache.myfaces.trinidad.render.CoreRenderer;
88  import org.apache.myfaces.trinidad.render.ExtendedRenderer;
89  import org.apache.myfaces.trinidad.render.LifecycleRenderer;
90  import org.apache.myfaces.trinidad.util.CollectionUtils;
91  import org.apache.myfaces.trinidad.util.ThreadLocalUtils;
92  
93  
94  /**
95   * Base implementation of components for all of Trinidad.  UIXComponentBase
96   * offers a number of features not supplied by the standard UIComponentBase
97   * class:
98   * <ul>
99   * <li>Use of FacesBean for better and easier state saving</li>
100  * <li>Support of the LifecycleRenderer class for greater Renderer
101  *  control over the lifecycle</li>
102  * <li>Built-in support for both the "partialTriggers" attribute
103  *   (declarative support for being a PPR target) and for triggering
104  *   such components (for being a the source of a PPR-causing event).</li>
105  * </ul>
106  * <h3>FacesBean and UIXComponentBase</h3>
107  * <p>
108  * UIXComponentBase differs from UIXComponent most particularly
109  * in its use of FacesBeans to store all state.  This offers
110  * a number of advantages:
111  * <ul>
112  * <li>Subclassers - if they use FacesBean for their state as well -
113  *   do not need to write overrides of saveState() and restoreState().
114  *   </li>
115  * <li>State is optimized by default</li>
116  * <li>Future optimizations - partly exposed today with
117  *    markInitialState() - can offer major state saving improvements.
118  * </ul>
119  * </p>
120  */
121 // TODO Write Class Javadoc
122 // TODO Thorough review against UIComponentBase
123 @JSFComponent
124 abstract public class UIXComponentBase extends UIXComponent
125 {
126   // Created up top to ensure it's present while we're processing
127   // class initialization code.
128   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(UIXComponentBase.class);
129 
130   static public final FacesBean.Type TYPE = _createType();
131   static public final PropertyKey ID_KEY =
132     TYPE.registerKey("id", String.class, PropertyKey.CAP_NOT_BOUND);
133   static public final PropertyKey RENDERED_KEY =
134     TYPE.registerKey("rendered", Boolean.class, Boolean.TRUE);
135   static public final PropertyKey BINDING_KEY =
136     TYPE.registerKey("binding");
137   static public final PropertyKey TRANSIENT_KEY =
138     TYPE.registerKey("transient", Boolean.class,
139                      PropertyKey.CAP_NOT_BOUND |
140                      PropertyKey.CAP_TRANSIENT);
141   static public final PropertyKey RENDERER_TYPE_KEY =
142     TYPE.registerKey("rendererType", String.class, PropertyKey.CAP_NOT_BOUND);
143   static private final PropertyKey _LISTENERS_KEY =
144     TYPE.registerKey("listeners", FacesListener[].class, PropertyKey.CAP_LIST);
145   static private final PropertyKey _ATTRIBUTE_CHANGE_LISTENER_KEY =
146     TYPE.registerKey("attributeChangeListener", MethodExpression.class);
147   static private final PropertyKey _CLIENT_BEHAVIORS_KEY =
148     TYPE.registerKey("clientBehaviors", AttachedObjects.class,
149                      PropertyKey.CAP_NOT_BOUND|PropertyKey.CAP_PARTIAL_STATE_HOLDER|PropertyKey.CAP_STATE_HOLDER);
150   static private final PropertyKey _SYSTEM_EVENT_LISTENERS_KEY =
151     TYPE.registerKey("systemEventListeners", AttachedObjects.class,
152                      PropertyKey.CAP_NOT_BOUND|PropertyKey.CAP_PARTIAL_STATE_HOLDER|PropertyKey.CAP_STATE_HOLDER);
153   static private final PropertyKey _COMPONENT_CHANGE_FILTERS_KEY =
154     TYPE.registerKey("componentChangeFilters", ComponentChangeFilter[].class, PropertyKey.CAP_LIST);
155   // =-=AEW "parent", "rendersChildren", "childCount", "children",
156   // "facets", "facetsAndChildren", "family" all are technically
157   // bean properties, but they aren't exposed here...
158 
159   static
160   {
161     // Register a couple of PropertyKeys against names that
162     // the RI's UIComponentTag implementation is shoving
163     // into all components.  This is purely an optimization, but
164     // a very useful one.
165     TYPE.registerKey("javax.faces.webapp.COMPONENT_IDS",
166                      List.class,
167                      PropertyKey.CAP_NOT_BOUND);
168     TYPE.registerKey("javax.faces.webapp.FACET_NAMES",
169                      List.class,
170                      PropertyKey.CAP_NOT_BOUND);
171 
172     // JSF hammers on this property during component pushing/popping.
173     // Register the PropertyKey to optimize property lookups.
174     TYPE.registerKey(Resource.COMPONENT_RESOURCE_KEY,
175                      PropertyKey.CAP_NOT_BOUND);
176 
177     TYPE.lock();
178   }
179 
180   public UIXComponentBase()
181   {
182   }
183 
184   public UIXComponentBase(String rendererType)
185   {
186     setRendererType(rendererType);
187   }
188 
189   protected FacesBean createFacesBean(
190     String rendererType)
191   {
192     FacesBean bean = FacesBeanFactory.createFacesBean(getClass(),
193                                                       rendererType);
194     UIXFacesBean uixBean = (UIXFacesBean) bean;
195     uixBean.init(this, getBeanType());
196     return uixBean;
197   }
198 
199   protected PropertyKey getPropertyKey(String name)
200   {
201     PropertyKey key = getBeanType().findKey(name);
202     if (key == null)
203       key = PropertyKey.createPropertyKey(name);
204 
205     return key;
206   }
207 
208   protected FacesBean.Type getBeanType()
209   {
210     return TYPE;
211   }
212 
213   @Override
214   public FacesBean getFacesBean()
215   {
216     if (_facesBean == null)
217       _init(null);
218 
219     return _facesBean;
220   }
221 
222   @Override
223   public String getContainerClientId(FacesContext context, UIComponent child)
224   {
225     return getContainerClientId(context);
226   }
227 
228   @Override
229   public void addAttributeChangeListener(AttributeChangeListener acl)
230   {
231     addFacesListener(acl);
232   }
233 
234   @Override
235   public void removeAttributeChangeListener(AttributeChangeListener acl)
236   {
237     removeFacesListener(acl);
238   }
239 
240   @Override
241   public AttributeChangeListener[] getAttributeChangeListeners()
242   {
243     return (AttributeChangeListener[])
244       getFacesListeners(AttributeChangeListener.class);
245   }
246 
247   @Override
248   public void setAttributeChangeListener(MethodExpression mb)
249   {
250     setProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY, mb);
251   }
252 
253   @Deprecated
254   public void setAttributeChangeListener(MethodBinding mb)
255   {
256     setAttributeChangeListener(adaptMethodBinding(mb));
257   }
258 
259   @Override
260   public MethodExpression getAttributeChangeListener()
261   {
262     return (MethodExpression) getProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY);
263   }
264 
265 
266   @Override
267   public ValueExpression getValueExpression(String name)
268   {
269     if (name == null)
270       throw new NullPointerException();
271 
272     PropertyKey key = getPropertyKey(name);
273 
274     // Support standard RI behavior where getValueBinding()
275     // doesn't complain about being asked for a ValueBinding -
276     // but continue supporting strict behavior at FacesBean layer.
277     if (!key.getSupportsBinding())
278       return null;
279 
280     return getFacesBean().getValueExpression(key);
281   }
282 
283   @Override
284   public void setValueExpression(String name,
285                                  ValueExpression expression)
286   {
287     if (name == null)
288       throw new NullPointerException();
289 
290     if ((expression != null) && expression.isLiteralText())
291     {
292       ELContext context =
293           FacesContext.getCurrentInstance().getELContext();
294       getAttributes().put(name, expression.getValue(context));
295     }
296     else
297     {
298       PropertyKey key = getPropertyKey(name);
299       getFacesBean().setValueExpression(key, expression);
300     }
301   }
302 
303   /**
304    */
305   @Override
306   public ValueBinding getValueBinding(String name)
307   {
308     if (name == null)
309       throw new NullPointerException();
310 
311     PropertyKey key = getPropertyKey(name);
312 
313     // Support standard RI behavior where getValueBinding()
314     // doesn't complain about being asked for a ValueBinding -
315     // but continue supporting strict behavior at FacesBean layer.
316     if (!key.getSupportsBinding())
317       return null;
318 
319     return getFacesBean().getValueBinding(key);
320   }
321 
322   @Override
323   public void setValueBinding(String name, ValueBinding binding)
324   {
325     if (name == null)
326       throw new NullPointerException();
327 
328     PropertyKey key = getPropertyKey(name);
329     getFacesBean().setValueBinding(key, binding);
330   }
331 
332   @Override
333   public Map<String, Object> getAttributes()
334   {
335     if (_attributes == null)
336       _init(null);
337 
338     return _attributes;
339   }
340 
341   /**
342    * Adds a change for a Component, or the Component's subtree, returning the change actually added,
343    * or <code>null</code>, if no change was added.  The proposed change may be rejected by the
344    * component itself, one of its ancestors, or the ChangeManager implementation.
345    * @param change     The change to add for this component
346    * @return The ComponentChange actually added, or
347    * <code>null</code> if no change was added.
348    * @see #addComponentChange(UIComponent, ComponentChange)
349    */
350   public final ComponentChange addComponentChange(ComponentChange change)
351   {
352     return addComponentChange(this, change);
353   }
354 
355   /**
356    * Add a component change filter to this component.
357    * When <code>addComponentChange(ComponentChange)</code> method on this component is called, the ComponentChange will
358    * be added only if it is accepted by all the component change filters attached to this component as well as those
359    * attached to all its ancestors.
360    * @param componentChangeFilter The ComponentChangeFilter instance to add to this component
361    * @see #addComponentChange(ComponentChange)
362    */
363   public final void addComponentChangeFilter(ComponentChangeFilter componentChangeFilter)
364   {
365     if (componentChangeFilter == null)
366       throw new NullPointerException();
367 
368     getFacesBean().addEntry(_COMPONENT_CHANGE_FILTERS_KEY, componentChangeFilter);
369   }
370 
371   /**
372    * Remove a component change filter to this component.
373    * @param componentChangeFilter The ComponentChangeFilter instance to remove from this component
374    * @see #addComponentChangeFilter(ComponentChangeFilter)
375    */
376   public final void removeComponentChangeFilter(ComponentChangeFilter componentChangeFilter)
377   {
378     if (componentChangeFilter == null)
379       throw new NullPointerException();
380 
381     getFacesBean().removeEntry(_COMPONENT_CHANGE_FILTERS_KEY, componentChangeFilter);
382   }
383 
384   /**
385   * Returns all the ComponentChangeFilters that are registered with this component.
386   *
387   * @return An array of registered ComponentChangeFilters
388   */
389   public final ComponentChangeFilter[] getComponentChangeFilters()
390   {
391     Iterator<ComponentChangeFilter> filterIter =
392       (Iterator<ComponentChangeFilter>)getFacesBean().entries(_COMPONENT_CHANGE_FILTERS_KEY);
393 
394     ArrayList<ComponentChangeFilter> filterList = CollectionUtils.arrayList(filterIter);
395     return filterList.toArray(new ComponentChangeFilter[filterList.size()]);
396   }
397 
398   @Override
399   protected Iterator<UIComponent> getRenderedFacetsAndChildren(
400     FacesContext facesContext)
401   {
402     _cacheRenderer(facesContext);
403     return super.getRenderedFacetsAndChildren(facesContext);
404   }
405 
406   /**
407    * Convenience method for implementors of {@link FlattenedComponent} to setup either the
408    * visiting context or the encoding context based on if the {@link ComponentProcessingContext}
409    * is processing for encoding or not.
410    * @param facesContext The faces context
411    * @param cpContext The component processing context passed to
412    *                  {@link FlattenedComponent#processFlattenedChildren}
413    */
414   protected void setupFlattenedContext(
415     FacesContext               facesContext,
416     ComponentProcessingContext cpContext
417     )
418   {
419     if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING))
420     {
421       setupEncodingContext(facesContext, RenderingContext.getCurrentInstance());
422     }
423     else
424     {
425       setupVisitingContext(facesContext);
426     }
427   }
428 
429   /**
430    * Convenience method for implementors of {@link FlattenedComponent} to setup either the
431    * visiting context or the encoding context based on if the {@link ComponentProcessingContext}
432    * is processing for encoding or not.
433    * @param facesContext The faces context
434    * @param cpContext The component processing context passed to
435    *                  {@link FlattenedComponent#processFlattenedChildren}
436    */
437   protected void setupFlattenedChildrenContext(
438     FacesContext               facesContext,
439     ComponentProcessingContext cpContext
440     )
441   {
442     if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING))
443     {
444       setupChildrenEncodingContext(facesContext, RenderingContext.getCurrentInstance());
445     }
446     else
447     {
448       setupChildrenVisitingContext(facesContext);
449     }
450   }
451 
452   /**
453    * Convenience method for implementors of {@link FlattenedComponent} to tear down either the
454    * visiting context or the encoding context based on if the {@link ComponentProcessingContext}
455    * is processing for encoding or not.
456    * @param facesContext The faces context
457    * @param cpContext The component processing context passed to
458    *                  {@link FlattenedComponent#processFlattenedChildren}
459    */
460   protected void tearDownFlattenedContext(
461     FacesContext               facesContext,
462     ComponentProcessingContext cpContext
463     )
464   {
465     if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING))
466     {
467       tearDownEncodingContext(facesContext, RenderingContext.getCurrentInstance());
468     }
469     else
470     {
471       tearDownVisitingContext(facesContext);
472     }
473   }
474 
475   /**
476    * Convenience method for implementors of {@link FlattenedComponent} to tear down either the
477    * visiting context or the encoding context based on if the {@link ComponentProcessingContext}
478    * is processing for encoding or not.
479    * @param facesContext The faces context
480    * @param cpContext The component processing context passed to
481    *                  {@link FlattenedComponent#processFlattenedChildren}
482    */
483   protected void tearDownFlattenedChildrenContext(
484     FacesContext               facesContext,
485     ComponentProcessingContext cpContext
486     )
487   {
488     if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING))
489     {
490       tearDownChildrenEncodingContext(facesContext, RenderingContext.getCurrentInstance());
491     }
492     else
493     {
494       tearDownChildrenVisitingContext(facesContext);
495     }
496   }
497 
498   // ------------------------------------------------------------- Properties
499 
500   /**
501    * Calculates the clientId for the component
502    */
503   private String _calculateClientId(FacesContext context)
504   {
505     // the clientId is always at least the id of the current component
506     // our implementation of getId() guarantees that this always
507     // returns a non-null value
508     String clientId = getId();
509 
510     // Search for an ancestor that is a naming container
511     UIComponent lastParent = null;
512     UIComponent currParent = getParent();
513 
514     while (true)
515     {
516       // prepend the NamingContainer portion of the id
517       if (currParent instanceof NamingContainer)
518       {
519         String contClientId;
520 
521         // Pass additional context information to naming containers which extend UIXComponent:
522         if (currParent instanceof UIXComponent)
523           contClientId = ((UIXComponent)currParent).getContainerClientId(context, this);
524         else
525           contClientId = currParent.getContainerClientId(context);
526 
527         StringBuilder bld = __getSharedStringBuilder();
528         bld.append(contClientId).append(NamingContainer.SEPARATOR_CHAR).append(clientId);
529         clientId = bld.toString();
530         break;
531       }
532       else if (currParent == null)
533       {
534         if (lastParent instanceof UIViewRoot)
535         {
536           // we got to the top of the component tree, so done looping
537           break;
538         }
539         else
540         {
541           // the component isn't in the component tree, which can cause the cached client id to be wrong.
542 
543           // =-= btsulliv see Trinidad-2374.  We can't do this right now because of a couple of bogus Trinidad Renderers
544           break;
545 
546           /*
547           throw new IllegalStateException("Calling getClientId() on component " + this +
548                                           " when it is not in the component tree.  Ancestor path:" +_getAncestorPath());
549           */
550         }
551       }
552 
553       lastParent = currParent;
554       currParent = lastParent.getParent();
555     }
556 
557     Renderer renderer = getRenderer(context);
558     if (null != renderer)
559       clientId = renderer.convertClientId(context, clientId);
560 
561     return clientId;
562   }
563 
564   private List<UIComponent> _getAncestors()
565   {
566     List<UIComponent> ancestors = new ArrayList<UIComponent>();
567 
568     UIComponent parent = getParent();
569 
570     while (parent != null)
571     {
572       ancestors.add(parent);
573 
574       parent = parent.getParent();
575     }
576 
577     Collections.reverse(ancestors);
578 
579     return ancestors;
580   }
581 
582   private String _getAncestorPath()
583   {
584     StringBuilder ancestorPath = new StringBuilder(1000);
585 
586     List<UIComponent> ancestors = _getAncestors();
587 
588     if (ancestors.isEmpty())
589     {
590       return "<none>";
591     }
592     else
593     {
594       Iterator<UIComponent> ancestorsIter = ancestors.iterator();
595 
596       boolean first = true;
597 
598       while (ancestorsIter.hasNext())
599       {
600         if (!first)
601         {
602           ancestorPath.append('/');
603         }
604         else
605         {
606           first = false;
607         }
608 
609         ancestorPath.append(ancestorsIter.next().toString());
610       }
611 
612       return ancestorPath.toString();
613     }
614   }
615 
616   @Override
617   public String getClientId(FacesContext context)
618   {
619     if (_isClientIdCachingEnabled())
620     {
621       String clientId = _clientId;
622 
623       if (clientId == null)
624       {
625         // This should not be called when the parent has not been set. Should it be
626         // called, the value will not be re-calculated correctly. This will be the case
627         // if someone attempts to invoke this function during facelets view construction.
628         if (_parent == null)
629         {
630           _LOG.warning("INVALID_CALL_TO_GETCLIENTID", getId());
631           // Return the value, even if not valid, for backward compatibility
632           return _calculateClientId(context);
633         }
634 
635         clientId = _calculateClientId(context);
636 
637         if (_usesFacesBeanImpl)
638         {
639           _clientId = clientId;
640         }
641       }
642       else if (_isClientIdDebuggingEnabled())
643       {
644         _warnClientIdCachingConfig(context);
645 
646         // for now validate success by checking the cached result against the dynamically
647         // generated result
648         String realID = _calculateClientId(context);
649 
650         if (!clientId.equals(realID))
651           throw new IllegalStateException(
652             String.format("Cached client id %s for %s doesn't match client id: %s",
653               clientId, this, realID));
654       }
655 
656       return clientId;
657     }
658     else
659     {
660       _warnClientIdCachingConfig(context);
661 
662       return _calculateClientId(context);
663     }
664   }
665 
666   /**
667    * Gets the identifier for the component.  This implementation
668    * never returns a null id.
669    */
670   @Override
671   public String getId()
672   {
673     // determine whether we can use the optimized code path or not
674     if (_usesFacesBeanImpl)
675     {
676       // optimized path
677 
678       // make sure that we always have an id
679       if (_id == null)
680       {
681         FacesContext context = FacesContext.getCurrentInstance();
682         UIViewRoot viewRoot = context.getViewRoot();
683 
684         _id = viewRoot.createUniqueId();
685       }
686 
687       return _id;
688     }
689     else
690     {
691       // unoptimized path
692       FacesBean facesBean = getFacesBean();
693 
694       String id = (String)facesBean.getProperty(ID_KEY);
695 
696       // make sure that we always have an id
697       if (id == null)
698       {
699         id = FacesContext.getCurrentInstance().getViewRoot().createUniqueId();
700 
701         facesBean.setProperty(ID_KEY, id);
702       }
703 
704       return id;
705     }
706   }
707 
708   /**
709    * Sets the identifier for the component.
710    * the identifier for the component.  Every component may be named by a component identifier that must conform to the following rules:
711    * <ul>
712    *   <li>They must start with a letter (as defined by the Character.isLetter() method) or underscore ( _ ).</li>
713    *   <li>Subsequent characters must be letters (as defined by the Character.isLetter() method), digits as defined by the Character.isDigit() method,
714    *   dashes ( - ), or underscores ( _ ).  To minimize the size of responses generated by JavaServer Faces, it is recommended that component identifiers
715    *   be as short as possible. If a component has been given an identifier, it must be unique in the namespace of the closest ancestor to that component
716    *   that is a NamingContainer (if any).
717    *   </li>
718    * </ul>
719    */
720   @Override
721   public void setId(String id)
722   {
723     FacesBean facesBean = getFacesBean();
724 
725     // if we are using a FacesBeanImpl, then the FacesBean will
726     // delegate all calls to set the id back to us and we can store
727     // the value localy.  Otehrwise,w e need to store it in
728     // the FacesBean
729     if (_usesFacesBeanImpl)
730     {
731       // only validate if the id has actually changed
732       if ((_id == null) || !_id.equals(id))
733       {
734         _validateId(id);
735         _id = id;
736 
737         // if we're a NamingContainer then changing our id will invalidate the clientIds of all
738         // of our children
739         if ((_clientId != null) && (this instanceof NamingContainer))
740         {
741           clearCachedClientIds();
742         }
743       }
744     }
745     else
746     {
747       _validateId(id);
748       facesBean.setProperty(ID_KEY, id);
749     }
750 
751     _clientId = null;
752   }
753 
754   /**
755    * Clears all of the cached clientIds in this component subtree
756    */
757   @Override
758   public void clearCachedClientIds()
759   {
760     // clear our clientId
761     _clientId = null;
762 
763     // clear the children
764     Iterator<UIComponent> allChildren = getFacetsAndChildren();
765 
766     while (allChildren.hasNext())
767     {
768       clearCachedClientIds(allChildren.next());
769     }
770   }
771 
772   @Override
773   abstract public String getFamily();
774 
775   @Override
776   public UIComponent getParent()
777   {
778     return _parent;
779   }
780 
781   /**
782    * <p>Set the parent <code>UIComponent</code> of this
783    * <code>UIComponent</code>.</p>
784    *
785    * @param parent The new parent, or <code>null</code> for the root node
786    *  of a component tree
787    */
788   @Override
789   public void setParent(UIComponent parent)
790   {
791     // do we add this component ?
792     if (parent != _parent)
793     {
794       if (parent != null)
795       {
796         // set the reference
797         _parent = parent;
798 
799         boolean isInView = parent.isInView();
800 
801         _resetClientId(isInView);
802 
803         if (isInView)
804         {
805           // trigger the ADD_EVENT and call setInView(true)
806           // recursive for all kids/facets...
807           // Application.publishEvent(java.lang.Class, java.lang.Object)  must be called, passing
808           // PostAddToViewEvent.class as the first argument and the newly added component as the second
809           // argument.
810           _publishPostAddToViewEvent(getFacesContext(), this);
811         }
812       }
813       else
814       {
815         boolean wasInView = _parent != null && _parent.isInView();
816 
817         if (wasInView)
818         {
819           // trigger the "remove event" lifecycle
820           // and call setInView(false) for all children/facets
821           // doing this => recursive
822           _publishPreRemoveFromViewEvent(getFacesContext(), this);
823         }
824 
825         // clear the references
826         _parent = null;
827         _resetClientId(false);
828       }
829     }
830   }
831 
832   private void _resetClientId(boolean isInView)
833   {
834     // clear cached client ids if necessary
835     if (_clientId != null)
836     {
837       String newClientId;
838 
839       // if the component is currently in the component tree, calculate the new clientId, to see if it has changed
840       if (isInView)
841       {
842         newClientId = _calculateClientId(FacesContext.getCurrentInstance());
843       }
844       else
845       {
846         newClientId = null;
847       }
848 
849       // if our clientId changed as a result of being reparented (because we moved
850       // between NamingContainers for instance) then we need to clear out
851       boolean clearCachedIds = !_clientId.equals(newClientId);
852 
853       // all of the cached client ids for our subtree
854       if (clearCachedIds)
855       {
856         clearCachedClientIds();
857         _clientId = newClientId;
858       }
859     }
860   }
861 
862   @Override
863   public boolean isRendered()
864   {
865     return getBooleanProperty(RENDERED_KEY, true);
866   }
867 
868   @Override
869   public void setRendered(boolean rendered)
870   {
871     setBooleanProperty(RENDERED_KEY, rendered);
872   }
873 
874   public boolean isTransient()
875   {
876     return getBooleanProperty(TRANSIENT_KEY, false);
877   }
878 
879   public void setTransient(boolean newTransient)
880   {
881     setBooleanProperty(TRANSIENT_KEY, newTransient);
882   }
883 
884   @Override
885   public String getRendererType()
886   {
887     // Don't create the FacesBean just to get the renderer type;
888     // Generally, rendererType will be the first property
889     // set, which will trigger the code below in setRendererType()
890     // to run correctly.
891     if (_facesBean == null)
892       return null;
893 
894     return (String) getProperty(RENDERER_TYPE_KEY);
895   }
896 
897   @Override
898   public void setRendererType(String rendererType)
899   {
900     String oldRendererType = getRendererType();
901     if (oldRendererType == null)
902     {
903       if (rendererType == null)
904         return;
905     }
906     else if (oldRendererType.equals(rendererType))
907     {
908       return;
909     }
910 
911     // prepare the faces bean
912     _init(rendererType);
913     setProperty(RENDERER_TYPE_KEY, rendererType);
914   }
915 
916   @Override
917   public boolean getRendersChildren()
918   {
919     Renderer renderer = getRenderer(getFacesContext());
920     if (renderer == null)
921       return false;
922 
923     return renderer.getRendersChildren();
924   }
925 
926   // ------------------------------------------------ Tree Management Methods
927 
928   @Override
929   public UIComponent findComponent(String id)
930   {
931     if (id == null)
932       throw new NullPointerException();
933 
934     if ("".equals(id))
935       throw new IllegalArgumentException();
936 
937     UIComponent from = this;
938     // If it starts with the separator character, it's
939     // an absolute path: start at the top
940     if (id.charAt(0) == NamingContainer.SEPARATOR_CHAR)
941     {
942       id = id.substring(1);
943       while (from.getParent() != null)
944         from = from.getParent();
945     }
946     // If it's a NamingContainer, start right here
947     else if (this instanceof NamingContainer)
948     {
949       ;
950     }
951     // Otherwise, go up to look for NamingContainer (or the top)
952     else
953     {
954       while (from.getParent() != null)
955       {
956         from = from.getParent();
957         if (from instanceof NamingContainer)
958           break;
959       }
960     }
961 
962     // Evaluate each part of the expression
963     String searchId;
964     int separatorIndex = id.indexOf(NamingContainer.SEPARATOR_CHAR);
965     if (separatorIndex < 0)
966       searchId = id;
967     else
968       searchId = id.substring(0, separatorIndex);
969 
970     if (searchId.equals(from.getId()))
971     {
972       // Don't need to look inside if we're already there
973       ;
974     }
975     else
976     {
977       from = _findInsideOf(from, searchId);
978     }
979 
980     // Final ID:  break, and return whatever we've found
981     if (separatorIndex < 0)
982     {
983       return from;
984     }
985     // Just an intermediate step.  Make sure we're at a NamingContainer,
986     // and then ask it to find the rest of the expression.
987     else
988     {
989       if (from == null)
990         return null;
991 
992       if (!(from instanceof NamingContainer))
993         throw new IllegalArgumentException();
994       return from.findComponent(id.substring(separatorIndex + 1));
995     }
996   }
997 
998   /**
999    * <p>Create (if necessary) and return a List of the children associated
1000    * with this component.</p>
1001    */
1002   @Override
1003   public List<UIComponent> getChildren()
1004   {
1005     if (_children == null)
1006       _children = new ChildArrayList(this);
1007 
1008     return _children;
1009   }
1010 
1011   @Override
1012   public int getChildCount()
1013   {
1014     if (_children == null)
1015       return 0;
1016     else
1017       return getChildren().size();
1018   }
1019 
1020   /**
1021    * <p>Create (if necessary) and return a Map of the facets associated
1022    * with this component.</p>
1023    */
1024   @Override
1025   public Map<String, UIComponent> getFacets()
1026   {
1027 
1028     if (_facets == null)
1029       _facets = new FacetHashMap(this);
1030 
1031     return _facets;
1032   }
1033 
1034   @Override
1035   public UIComponent getFacet(String facetName)
1036   {
1037     if (facetName == null)
1038       throw new NullPointerException();
1039 
1040     if (_facets == null)
1041       return null;
1042     else
1043       return getFacets().get(facetName);
1044   }
1045 
1046   /**
1047    * Returns an Iterator over the names of all facets.
1048    * Unlike getFacets().keySet().iterator(), this does
1049    * not require instantiating a Map if there are
1050    * no facets.  (Note that this is not part of the
1051    * UIComponent API.)
1052    */
1053   public Iterator<String> getFacetNames()
1054   {
1055     if (_facets == null)
1056       return _EMPTY_STRING_ITERATOR;
1057     else
1058       return _facets.keySet().iterator();
1059   }
1060 
1061   @Override
1062   public Iterator<UIComponent> getFacetsAndChildren()
1063   {
1064     // =-=AEW Is this supposed to be an immutable Iterator?
1065     if (_facets == null)
1066     {
1067       if (_children == null)
1068         return _EMPTY_UICOMPONENT_ITERATOR;
1069 
1070       return _children.iterator();
1071     }
1072     else
1073     {
1074       if (_children == null)
1075         return _facets.values().iterator();
1076     }
1077 
1078     return new CompositeIterator<UIComponent>(_children.iterator(), _facets.values().iterator());
1079   }
1080 
1081   // ------------------------------------------- Event processing methods
1082 
1083   @Override
1084   public void broadcast(FacesEvent event)
1085     throws AbortProcessingException
1086   {
1087     if (event == null)
1088       throw new NullPointerException();
1089 
1090     if (_LOG.isFine())
1091       _LOG.fine("Broadcasting event " + event + " to " + this);
1092 
1093     UIComponent component = event.getComponent();
1094 
1095     FacesContext context = getFacesContext();
1096     assert _isCurrentComponent(context, component);
1097     assert _isCompositeParentCurrent(context, component);
1098 
1099     if (component != null && satisfiesPartialTrigger(event))
1100     {
1101       RequestContext adfContext = RequestContext.getCurrentInstance();
1102       if (adfContext != null)
1103         adfContext.partialUpdateNotify(component);
1104     }
1105 
1106     Renderer renderer = getRenderer(context);
1107     if (renderer instanceof CoreRenderer)
1108     {
1109       // Allow the renderer to handle the event
1110       ((CoreRenderer)renderer).broadcast(this, event);
1111     }
1112 
1113     Iterator<FacesListener> iter =
1114       (Iterator<FacesListener>)getFacesBean().entries(_LISTENERS_KEY);
1115 
1116     while (iter.hasNext())
1117     {
1118       FacesListener listener = iter.next();
1119       if (event.isAppropriateListener(listener))
1120       {
1121         event.processListener(listener);
1122       }
1123     }
1124 
1125     if (event instanceof AttributeChangeEvent)
1126     {
1127       broadcastToMethodExpression(event, getAttributeChangeListener());
1128     }
1129   }
1130 
1131   /**
1132    * Check if a faces event broadcast to this component should trigger the partial updates of the
1133    * target listeners of this component. By default, all events trigger a partial update of the listeners.
1134    *
1135    * @param event The event to check
1136    * @return true if the partial triggers should be updated by this event being broadcast
1137    */
1138   protected boolean satisfiesPartialTrigger(
1139     FacesEvent event)
1140   {
1141     return true;
1142   }
1143 
1144   // ------------------------------------------- Lifecycle Processing Methods
1145 
1146   @Override
1147   public void decode(FacesContext context)
1148   {
1149     if (context == null)
1150       throw new NullPointerException();
1151 
1152     // Find all the partialTriggers and save on the context
1153     Map<String, Object> attrs = getAttributes();
1154     Object triggers = attrs.get("partialTriggers");
1155     if (triggers instanceof String[])
1156     {
1157       RequestContext adfContext = RequestContext.getCurrentInstance();
1158       if (adfContext != null)
1159         adfContext.addPartialTriggerListeners(this, (String[]) triggers);
1160     }
1161 
1162     __rendererDecode(context);
1163   }
1164 
1165   @Override
1166   public void encodeBegin(FacesContext context) throws IOException
1167   {
1168     if (context == null)
1169       throw new NullPointerException();
1170 
1171     // Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
1172     pushComponentToEL(context, this);
1173 
1174     if (!isRendered())
1175       return;
1176 
1177     context.getApplication().publishEvent(context,  PreRenderComponentEvent.class, UIComponent.class, this);
1178 
1179     _cacheRenderer(context);
1180     Renderer renderer = getRenderer(context);
1181 
1182     // if there is a Renderer for this component
1183     if (renderer != null)
1184     {
1185       renderer.encodeBegin(context, this);
1186     }
1187   }
1188 
1189   @Override
1190   public void encodeChildren(FacesContext context) throws IOException
1191   {
1192     if (context == null)
1193       throw new NullPointerException();
1194 
1195     if (!isRendered())
1196       return;
1197 
1198     Renderer renderer = getRenderer(context);
1199     // if there is a Renderer for this component
1200     if (renderer != null)
1201     {
1202       renderer.encodeChildren(context, this);
1203     }
1204   }
1205 
1206   @Override
1207   public void encodeEnd(FacesContext context) throws IOException
1208   {
1209     if (context == null)
1210       throw new NullPointerException();
1211 
1212     try
1213     {
1214     if (isRendered())
1215     {
1216       RequestContext requestContext = RequestContext.getCurrentInstance();
1217       requestContext.pushCurrentComponent(context, this);
1218 
1219       try
1220       {
1221         Renderer renderer = getRenderer(context);
1222         // if there is a Renderer for this component
1223         if (renderer != null)
1224         {
1225           renderer.encodeEnd(context, this);
1226         }
1227       }
1228       finally
1229       {
1230         requestContext.popCurrentComponent(context, this);
1231       }
1232     }
1233   }
1234     finally
1235     {
1236       popComponentFromEL(context);
1237     }
1238   }
1239 
1240   @Override
1241   public void queueEvent(FacesEvent event)
1242   {
1243     if (event == null)
1244       throw new NullPointerException();
1245 
1246     UIComponent parent = getParent();
1247     if (parent == null)
1248       throw new IllegalStateException();
1249 
1250     parent.queueEvent(event);
1251   }
1252 
1253   // ----------------------------------------------- Lifecycle Phase Handlers
1254 
1255   @Override
1256   public void processDecodes(FacesContext context)
1257   {
1258     if (context == null)
1259       throw new NullPointerException();
1260 
1261     if (!isRendered())
1262       return;
1263 
1264     RequestContext requestContext = RequestContext.getCurrentInstance();
1265     requestContext.pushCurrentComponent(context, this);
1266     pushComponentToEL(context, this);
1267 
1268     try
1269     {
1270       // Process all facets and children of this component
1271       decodeChildren(context);
1272 
1273       // Process this component itself
1274       decode(context);
1275     }
1276     finally
1277     {
1278       // Call UIComponent.popComponentFromEL(javax.faces.context.FacesContext) from inside of a finally
1279       // block, just before returning.
1280 
1281       popComponentFromEL(context);
1282       requestContext.popCurrentComponent(context, this);
1283     }
1284   }
1285 
1286   @Override
1287   public void processValidators(FacesContext context)
1288   {
1289     if (context == null)
1290       throw new NullPointerException();
1291 
1292     if (!isRendered())
1293       return;
1294 
1295     RequestContext requestContext = RequestContext.getCurrentInstance();
1296     requestContext.pushCurrentComponent(context, this);
1297     pushComponentToEL(context, this);
1298 
1299     try
1300     {
1301       // Process all facets and children of this component
1302       validateChildren(context);
1303     }
1304     finally
1305     {
1306       popComponentFromEL(context);
1307       requestContext.popCurrentComponent(context, this);
1308     }
1309   }
1310 
1311   @Override
1312   public void processUpdates(FacesContext context)
1313   {
1314     if (context == null)
1315       throw new NullPointerException();
1316 
1317     if (!isRendered())
1318       return;
1319 
1320     RequestContext requestContext = RequestContext.getCurrentInstance();
1321     requestContext.pushCurrentComponent(context, this);
1322     pushComponentToEL(context, this);
1323 
1324     try
1325     {
1326       // Process all facets and children of this component
1327       updateChildren(context);
1328     }
1329     finally
1330     {
1331       popComponentFromEL(context);
1332       requestContext.popCurrentComponent(context, this);
1333     }
1334   }
1335 
1336   @Override
1337   public Object processSaveState(FacesContext context)
1338   {
1339     if (context == null)
1340       throw new NullPointerException();
1341 
1342     if (_LOG.isFiner())
1343       _LOG.finer("processSaveState() on " + this);
1344 
1345     RequestContext requestContext = RequestContext.getCurrentInstance();
1346     requestContext.pushCurrentComponent(context, this);
1347     pushComponentToEL(context, this);
1348 
1349     Object state = null;
1350 
1351     try
1352     {
1353       if (((_children == null) || _children.isEmpty()) &&
1354           ((_facets == null) || _facets.isEmpty()))
1355       {
1356         state = saveState(context);
1357       }
1358       else
1359       {
1360         TreeState treeState = new TreeState();
1361         treeState.saveState(context, this);
1362         if (treeState.isEmpty())
1363           state = null;
1364 
1365         state = treeState;
1366       }
1367     }
1368     catch (RuntimeException e)
1369     {
1370       _LOG.warning(_LOG.getMessage("COMPONENT_CHILDREN_SAVED_STATE_FAILED", this));
1371 
1372       throw e;
1373     }
1374 
1375     finally
1376     {
1377       popComponentFromEL(context);
1378       requestContext.popCurrentComponent(context, this);
1379     }
1380 
1381     return state;
1382   }
1383 
1384   // TODO  will have deep problems if UIComponent.saveState() ever
1385   //   returns a String.
1386   // TODO crashes and burns if there are fewer children or missing
1387   //  facets from when state was saved.
1388   @Override
1389   public void processRestoreState(FacesContext context, Object state)
1390   {
1391     if (context == null)
1392       throw new NullPointerException();
1393 
1394     if (_LOG.isFiner())
1395       _LOG.finer("processRestoreState() on " + this);
1396 
1397     RequestContext requestContext = RequestContext.getCurrentInstance();
1398     requestContext.pushCurrentComponent(context, this);
1399     pushComponentToEL(context, this);
1400 
1401     try
1402     {
1403       // If we saved a "TreeState", use it to restore everything
1404       if (state instanceof TreeState)
1405       {
1406         ((TreeState) state).restoreState(context, this);
1407       }
1408       // Otherwise, we had no children or facets, and just use
1409       // the "state" object
1410       else
1411       {
1412         restoreState(context, state);
1413       }
1414     }
1415     finally
1416     {
1417       popComponentFromEL(context);
1418       requestContext.popCurrentComponent(context, this);
1419     }
1420   }
1421 
1422   @Override
1423   public void markInitialState()
1424   {
1425     // -= Simon Lessard =-
1426     // FIXME: Set to true, but never read
1427     //_initialStateMarked = true;
1428     getFacesBean().markInitialState();
1429   }
1430 
1431   @Override
1432   public void clearInitialState()
1433   {
1434     getFacesBean().clearInitialState();
1435   }
1436 
1437   @Override
1438   public boolean initialStateMarked()
1439   {
1440     return getFacesBean().initialStateMarked();
1441   }
1442 
1443   public Object saveState(FacesContext facesContext)
1444   {
1445     Object state = getFacesBean().saveState(facesContext);
1446 
1447     // if component state serialization checking is on, attempt to Serialize the
1448     // component state immediately in order to determine which component's state
1449     // failed state saving.
1450     if (StateUtils.checkComponentStateSerialization(facesContext))
1451     {
1452       try
1453       {
1454         new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(state);
1455       }
1456       catch (IOException e)
1457       {
1458         throw new RuntimeException(_LOG.getMessage("COMPONENT_SAVED_STATE_FAILED", this), e);
1459       }
1460     }
1461 
1462     return state;
1463   }
1464 
1465   public void restoreState(
1466     FacesContext facesContext,
1467     Object       stateObj)
1468   {
1469     getFacesBean().restoreState(facesContext, stateObj);
1470   }
1471 
1472   @Override
1473   public String toString()
1474   {
1475     String className = getClass().getName();
1476     int periodIndex = className.lastIndexOf('.');
1477     if (periodIndex >= 0)
1478       className = className.substring(periodIndex + 1);
1479 
1480     return className + "[" + getFacesBean().toString() + ", id=" + getId() + "]";
1481   }
1482 
1483   /**
1484    * <p>Return the {@link FacesContext} instance for the current request.</p>
1485    */
1486   @Override
1487   protected FacesContext getFacesContext()
1488   {
1489     // If we ever have a way for a component to get notified
1490     // when it's finished being used for a given request,
1491     // we could cache this as an instance variable.
1492     return FacesContext.getCurrentInstance();
1493   }
1494 
1495   /**
1496    * Delegates to LifecycleRenderer, if present,
1497    * otherwise calls decodeChildrenImpl.
1498    *
1499    * @param context the current FacesContext
1500    */
1501   final protected void decodeChildren(FacesContext context)
1502   {
1503     LifecycleRenderer renderer = getLifecycleRenderer(context);
1504     // if there is a HierarchyRenderer for this component
1505     if (renderer != null)
1506     {
1507       if (renderer.decodeChildren(context, this))
1508         return;
1509     }
1510 
1511     decodeChildrenImpl(context);
1512   }
1513 
1514   /**
1515    * Calls processDecodes on all facets and children of this
1516    * component.
1517    * @param context the current FacesContext
1518    */
1519   protected void decodeChildrenImpl(FacesContext context)
1520   {
1521     Iterator<UIComponent> kids = getRenderedFacetsAndChildren(context);
1522     while (kids.hasNext())
1523     {
1524       UIComponent kid = kids.next();
1525       kid.processDecodes(context);
1526     }
1527   }
1528 
1529   /**
1530    * Delegates to LifecycleRenderer, if present,
1531    * otherwise calls validateChildrenImpl.
1532    *
1533    * @param context the current FacesContext
1534    */
1535   final protected void validateChildren(FacesContext context)
1536   {
1537     LifecycleRenderer renderer = getLifecycleRenderer(context);
1538     // if there is a ExtendedRenderer for this component
1539     if (renderer != null)
1540     {
1541       if (renderer.validateChildren(context, this))
1542         return;
1543     }
1544 
1545     validateChildrenImpl(context);
1546   }
1547 
1548   /**
1549    * Calls processValidators on all facets and children of this
1550    * component.
1551    * @param context the current FacesContext
1552    */
1553   protected void validateChildrenImpl(FacesContext context)
1554   {
1555     // Process all the facets and children of this component
1556     Iterator<UIComponent> kids = getRenderedFacetsAndChildren(context);
1557     while (kids.hasNext())
1558     {
1559       UIComponent kid = kids.next();
1560       kid.processValidators(context);
1561     }
1562   }
1563 
1564   /**
1565    * Delegates to LifecycleRenderer, if present,
1566    * otherwise calls upateChildrenImpl.
1567    *
1568    * @param context the current FacesContext
1569    */
1570   final protected void updateChildren(FacesContext context)
1571   {
1572     LifecycleRenderer renderer = getLifecycleRenderer(context);
1573     // if there is a ExtendedRenderer for this component
1574     if (renderer != null)
1575     {
1576       if (renderer.updateChildren(context, this))
1577         return;
1578     }
1579 
1580     updateChildrenImpl(context);
1581   }
1582 
1583   protected void updateChildrenImpl(FacesContext context)
1584   {
1585     // Process all the facets and children of this component
1586     Iterator<UIComponent> kids = getRenderedFacetsAndChildren(context);
1587     while (kids.hasNext())
1588     {
1589       UIComponent kid = kids.next();
1590       kid.processUpdates(context);
1591     }
1592   }
1593 
1594   @Override
1595   protected void addFacesListener(FacesListener listener)
1596   {
1597     if (listener == null)
1598       throw new NullPointerException();
1599 
1600     getFacesBean().addEntry(_LISTENERS_KEY, listener);
1601   }
1602 
1603   @Override
1604   protected void removeFacesListener(FacesListener listener)
1605   {
1606     if (listener == null)
1607       throw new NullPointerException();
1608 
1609     getFacesBean().removeEntry(_LISTENERS_KEY, listener);
1610   }
1611 
1612   @Override
1613   protected FacesListener[] getFacesListeners(Class clazz)
1614   {
1615     if (clazz == null)
1616       throw new NullPointerException();
1617 
1618     if (!FacesListener.class.isAssignableFrom(clazz))
1619       throw new IllegalArgumentException();
1620 
1621     return (FacesListener[])
1622        getFacesBean().getEntries(_LISTENERS_KEY, clazz);
1623   }
1624 
1625   /**
1626    * Checks if any of the ComponentChangeFilter instances that is attached to this component rejects the supplied
1627    * change for the supplied component.
1628    */
1629   private boolean _isAnyFilterRejectingChange(UIComponent uic, ComponentChange cc)
1630   {
1631     // assume we accept the change
1632     boolean rejectsChange = false;
1633 
1634     Iterator<ComponentChangeFilter> iter =
1635       (Iterator<ComponentChangeFilter>)getFacesBean().entries(_COMPONENT_CHANGE_FILTERS_KEY);
1636 
1637     while (iter.hasNext())
1638     {
1639       ComponentChangeFilter currentFilter = iter.next();
1640       if (currentFilter.accept(cc, uic) == ComponentChangeFilter.Result.REJECT)
1641       {
1642         // one of the filter rejected the change, look no further
1643         rejectsChange = true;
1644         break;
1645       }
1646     }
1647 
1648     return rejectsChange;
1649   }
1650 
1651   private UIXComponentBase _getNextUIXComponentBaseAnxcestor()
1652   {
1653     UIComponent parent = getParent();
1654 
1655     while (parent != null)
1656     {
1657       if (parent instanceof UIXComponentBase)
1658       {
1659         return (UIXComponentBase)parent;
1660       }
1661 
1662       parent = parent.getParent();
1663     }
1664 
1665     return null;
1666   }
1667 
1668   /**
1669    * Called when adding a change to a Component, or the Component's subtree.
1670    * The default implementation delegates the call to the parent, if possible, otherwise
1671    * it adds the change to the ChangeManager directly.
1672    * Subclasses can override this method to among other things, filter or transform the changes.
1673    * @param component  The component that the change is for
1674    * @param change     The change to add for this component
1675    * @return The ComponentChange actually added, or
1676    * <code>null</code> if no change was added.
1677    * @see #addComponentChange(ComponentChange)
1678    * @see #addAttributeChange
1679    */
1680   protected ComponentChange addComponentChange(UIComponent component, ComponentChange change)
1681   {
1682     // check moved from addAttributeChange(), as this is more central
1683     if ((component == this) && (change instanceof AttributeComponentChange))
1684     {
1685       AttributeComponentChange aa             = (AttributeComponentChange)change;
1686       Object                   attributeValue = aa.getAttributeValue();
1687 
1688       if (attributeValue instanceof RowKeySet)
1689       {
1690         change = new RowKeySetAttributeChange(getClientId(getFacesContext()),
1691                                               aa.getAttributeName(),
1692                                               attributeValue);
1693       }
1694     }
1695 
1696     // add the change unless we have a change filter that is attached to this component wants to supress the change
1697     if (!_isAnyFilterRejectingChange(component, change))
1698     {
1699       UIXComponentBase nextUIXParent = _getNextUIXComponentBaseAnxcestor();
1700 
1701       if (nextUIXParent != null)
1702       {
1703         return nextUIXParent.addComponentChange(component, change);
1704       }
1705       else
1706       {
1707         RequestContext trinContext = RequestContext.getCurrentInstance();
1708         trinContext.getChangeManager().addComponentChange(getFacesContext(), component, change);
1709         return change;
1710       }
1711     }
1712     else
1713     {
1714       return null;
1715     }
1716   }
1717 
1718 
1719   /**
1720    * Convenience function for
1721    * <code>addComponentChange(new AttributeComponentChange(attributeName, attributeValue));</code>
1722    * This function is not <code>final</code> for backwards compatibility reasons, however,
1723    * existing subclassers whould override <code>addComponentChange</code> instead.
1724    * @param attributeName
1725    * @param attributeValue
1726    * @see #addComponentChange(UIComponent, ComponentChange)
1727    */
1728   protected void addAttributeChange(
1729     String attributeName,
1730     Object attributeValue)
1731   {
1732     addComponentChange(new AttributeComponentChange(attributeName, attributeValue));
1733   }
1734 
1735   void __rendererDecode(FacesContext context)
1736   {
1737     _cacheRenderer(context);
1738     Renderer renderer = getRenderer(context);
1739     // if there is a Renderer for this component
1740     if (renderer != null)
1741     {
1742       renderer.decode(context, this);
1743     }
1744   }
1745 
1746   /**
1747    * Publish PostAddToViewEvent to the component and all facets and children.
1748    *
1749    * @param context the current FacesContext
1750    * @param component the current UIComponent
1751    */
1752   private void _publishPostAddToViewEvent(
1753     FacesContext context,
1754     UIComponent component)
1755   {
1756     component.setInView(true);
1757     context.getApplication().publishEvent(context, PostAddToViewEvent.class, UIComponent.class, component);
1758 
1759     if (component.getChildCount() > 0)
1760     {
1761       List<UIComponent> children = component.getChildren();
1762       UIComponent child = null;
1763       UIComponent currentChild = null;
1764       int i = 0;
1765       while (i < children.size())
1766       {
1767         child = children.get(i);
1768 
1769         // Iterate over the same index if the component was removed
1770         // This prevents skip components when processing
1771         do
1772         {
1773           _publishPostAddToViewEvent(context, child);
1774           currentChild = child;
1775         }
1776         while ((i < children.size()) &&
1777                ((child = children.get(i)) != currentChild) );
1778         i++;
1779       }
1780     }
1781 
1782     if (component.getFacetCount() > 0)
1783     {
1784       for (UIComponent child : component.getFacets().values())
1785       {
1786         _publishPostAddToViewEvent(context, child);
1787       }
1788     }
1789   }
1790 
1791   /**
1792    * Publish PreRemoveFromViewEvent to the component and all facets and children.
1793    *
1794    * @param context the current FacesContext
1795    * @param component the current UIComponent
1796    */
1797   private void _publishPreRemoveFromViewEvent(
1798     FacesContext context,
1799     UIComponent component)
1800   {
1801     context.getApplication().publishEvent(context, PreRemoveFromViewEvent.class, UIComponent.class, component);
1802 
1803     if (component.getChildCount() > 0)
1804     {
1805       for (UIComponent child : component.getChildren())
1806       {
1807         _publishPreRemoveFromViewEvent(context, child);
1808       }
1809     }
1810 
1811     if (component.getFacetCount() > 0)
1812     {
1813       for (UIComponent child : component.getFacets().values())
1814       {
1815         _publishPreRemoveFromViewEvent(context, child);
1816       }
1817     }
1818 
1819     component.setInView(false);
1820   }
1821 
1822   private void _cacheRenderer(FacesContext context)
1823   {
1824     Renderer renderer = _getRendererImpl(context);
1825     _cachedRenderer = renderer;
1826 
1827     // cache the lifecycle renderer
1828     if (renderer instanceof LifecycleRenderer)
1829     {
1830       _cachedLifecycleRenderer = (LifecycleRenderer)renderer;
1831     }
1832     else
1833     {
1834       _cachedLifecycleRenderer = null;
1835     }
1836   }
1837 
1838   private Renderer _getRendererImpl(FacesContext context)
1839   {
1840     String rendererType = getRendererType();
1841     if (rendererType != null)
1842     {
1843       RenderKit renderKit = context.getRenderKit();
1844       Renderer renderer = renderKit.getRenderer(getFamily(), rendererType);
1845       if (renderer == null)
1846       {
1847         _LOG.warning("CANNOT_FIND_RENDERER", new Object[]{this, rendererType});
1848       }
1849 
1850       return renderer;
1851     }
1852 
1853     return null;
1854   }
1855 
1856   private LifecycleRenderer _getLifecycleRendererImpl(FacesContext context)
1857   {
1858     Renderer renderer = _getRendererImpl(context);
1859     if (renderer instanceof LifecycleRenderer)
1860     {
1861       return (LifecycleRenderer)renderer;
1862     }
1863 
1864     return null;
1865   }
1866 
1867   private boolean _isCurrentComponent(FacesContext context, UIComponent component)
1868   {
1869     return component.equals(UIComponent.getCurrentComponent(context));
1870   }
1871 
1872   private boolean _isCompositeParentCurrent(FacesContext context, UIComponent component)
1873   {
1874     UIComponent currentCompositeComponent = UIComponent.getCurrentCompositeComponent(context);
1875     if(UIComponent.isCompositeComponent(component))
1876     {
1877       return component.equals(currentCompositeComponent);
1878     }
1879     else
1880     {
1881       UIComponent compositeParent = UIComponent.getCompositeComponentParent(component);
1882       if(compositeParent == null)
1883       {
1884         return currentCompositeComponent == null;
1885       }
1886       else
1887       {
1888         return compositeParent.equals(currentCompositeComponent);
1889       }
1890     }
1891   }
1892 
1893   @Override
1894   protected Renderer getRenderer(FacesContext context)
1895   {
1896     Renderer renderer = _cachedRenderer;
1897     if (renderer != _UNDEFINED_RENDERER)
1898       return renderer;
1899 
1900     return _getRendererImpl(context);
1901   }
1902 
1903   protected LifecycleRenderer getLifecycleRenderer(FacesContext context)
1904   {
1905     LifecycleRenderer renderer = _cachedLifecycleRenderer;
1906     if (renderer != _UNDEFINED_LIFECYCLE_RENDERER)
1907       return renderer;
1908 
1909     return _getLifecycleRendererImpl(context);
1910 
1911   }
1912 
1913   protected void setProperty(PropertyKey key, Object value)
1914   {
1915     getFacesBean().setProperty(key, value);
1916   }
1917 
1918   protected Object getProperty(PropertyKey key)
1919   {
1920     return getFacesBean().getProperty(key);
1921   }
1922 
1923   protected void setBooleanProperty(PropertyKey key, boolean value)
1924   {
1925     getFacesBean().setProperty(key, value ? Boolean.TRUE : Boolean.FALSE);
1926   }
1927 
1928   protected boolean getBooleanProperty(PropertyKey key, boolean defaultValue)
1929   {
1930     Object o = getFacesBean().getProperty(key);
1931     if (defaultValue)
1932       return !Boolean.FALSE.equals(o);
1933     else
1934       return Boolean.TRUE.equals(o);
1935   }
1936 
1937   protected void setIntProperty(PropertyKey key, int value)
1938   {
1939     getFacesBean().setProperty(key, Integer.valueOf(value));
1940   }
1941 
1942   protected int getIntProperty(PropertyKey key, int defaultValue)
1943   {
1944     Number n = (Number) getFacesBean().getProperty(key);
1945     if (n == null)
1946       return defaultValue;
1947 
1948     return n.intValue();
1949   }
1950 
1951   /**
1952    * Return the number of facets.  This is more efficient than
1953    * calling getFacets().size();
1954    */
1955   @Override
1956   public int getFacetCount()
1957   {
1958     if (_facets == null)
1959       return 0;
1960 
1961     return _facets.size();
1962   }
1963 
1964   /**
1965    * Broadcast an event to a MethodBinding.
1966    * This can be used to support MethodBindings such as the "actionListener"
1967    * binding on ActionSource components:
1968    * &lt;tr:commandButton actionListener="#{mybean.myActionListener}">
1969    * @deprecated
1970    */
1971   protected final void broadcastToMethodBinding(
1972     FacesEvent event,
1973     MethodBinding method) throws AbortProcessingException
1974   {
1975     if (method != null)
1976     {
1977       try
1978       {
1979         FacesContext context = getFacesContext();
1980         method.invoke(context, new Object[] { event });
1981       }
1982       catch (EvaluationException ee)
1983       {
1984         // Checking for AbortProcessingExceptions, and unwrapping
1985         // it if the underlying exception is AbortProcessingExceptions.
1986         Throwable currentThrowable = ee.getCause();
1987         while (currentThrowable != null)
1988         {
1989           if (currentThrowable instanceof AbortProcessingException)
1990           {
1991             throw ((AbortProcessingException)currentThrowable);
1992           }
1993           currentThrowable = currentThrowable.getCause();
1994         }
1995         throw ee;
1996       }
1997     }
1998   }
1999 
2000   /**
2001    * Given a MethodBinding, create a MethodExpression that
2002    * adapts it.
2003    */
2004   static public MethodExpression adaptMethodBinding(MethodBinding binding)
2005   {
2006     return new MethodBindingMethodExpression(binding);
2007   }
2008 
2009   /**
2010    * Broadcast an event to a MethodExpression.
2011    * This can be used to support MethodBindings such as the "actionListener"
2012    * binding on ActionSource components:
2013    * &lt;tr:commandButton actionListener="#{mybean.myActionListener}">
2014    */
2015   protected final void broadcastToMethodExpression(
2016     FacesEvent event,
2017     MethodExpression method) throws AbortProcessingException
2018   {
2019     if (method != null)
2020     {
2021       try
2022       {
2023         FacesContext context = getFacesContext();
2024         method.invoke(context.getELContext(), new Object[] { event });
2025       }
2026       catch (ELException ee)
2027       {
2028         Throwable t = ee.getCause();
2029         // Unwrap AbortProcessingExceptions
2030         if (t instanceof AbortProcessingException)
2031           throw ((AbortProcessingException) t);
2032         throw ee;
2033       }
2034     }
2035   }
2036 
2037   /**
2038    * Convenience method to call <code>invokeOnComponent</code> on all of the
2039    * children of a component, surrounding the invocation with calls to
2040    * <code>setup/tearDownChildrenVisitingContext</code>.
2041    * This is useful when a component sometimes optimizes
2042    * away calling <code>invokeOnComponent</code> on its children.
2043    * @see UIXComponent#setupChildrenVisitingContext
2044    * @see UIXComponent#tearDownChildrenVisitingContext
2045    */
2046   protected final boolean invokeOnChildrenComponents(
2047     FacesContext context,
2048     String clientId,
2049     ContextCallback callback)
2050     throws FacesException
2051   {
2052     setupChildrenVisitingContext(context);
2053 
2054     boolean found = false;
2055 
2056     try
2057     {
2058       Iterator<UIComponent> children = getFacetsAndChildren();
2059 
2060       while (children.hasNext() && !found)
2061       {
2062         found = children.next().invokeOnComponent(context, clientId, callback);
2063       }
2064     }
2065     finally
2066     {
2067       tearDownChildrenVisitingContext(context);
2068     }
2069 
2070     return found;
2071   }
2072 
2073   /**
2074    * <p>
2075    * Optimized implementation of <code>invokeOnComponent</code> for NamingContainers.
2076    * If the clientId isn't within the NamingContainer, invocation of the
2077    * NamingContainer's children is skipped.
2078    * </p>
2079    * <p>Subclasses implementing NamingContainer should override
2080    * <code>invokeOnComponent</code> and delegate to this method.</p>
2081    */
2082   protected final boolean invokeOnNamingContainerComponent(
2083     FacesContext context,
2084     String clientId,
2085     ContextCallback callback)
2086     throws FacesException
2087   {
2088     assert this instanceof NamingContainer : "Only use invokeOnNamingContainerComponent on NamingContainers";
2089 
2090     boolean invokedComponent;
2091 
2092     setupVisitingContext(context);
2093 
2094     try
2095     {
2096       String thisClientId = getClientId(context);
2097 
2098       if (clientId.equals(thisClientId))
2099       {
2100         RequestContext requestContext = RequestContext.getCurrentInstance();
2101         requestContext.pushCurrentComponent(context, this);
2102         pushComponentToEL(context, null);
2103 
2104         try
2105         {
2106           // this is the component we want, so invoke the callback
2107           callback.invokeContextCallback(context, this);
2108         }
2109         finally
2110         {
2111           popComponentFromEL(context);
2112           requestContext.popCurrentComponent(context, this);
2113         }
2114 
2115         invokedComponent = true;
2116       }
2117       else
2118       {
2119         // if this is a NamingContainer, only traverse into it if the clientId we are looking for
2120         // is inside of it
2121         if ((!clientId.startsWith(thisClientId) ||
2122             (clientId.charAt(thisClientId.length()) != NamingContainer.SEPARATOR_CHAR)))
2123         {
2124           invokedComponent = false;
2125         }
2126         else
2127         {
2128           // iterate through children.
2129           // We inline this code instead of calling super in order
2130           // to avoid making an extra call to getClientId().
2131           invokedComponent = invokeOnChildrenComponents(context, clientId, callback);
2132         }
2133       }
2134     }
2135     finally
2136     {
2137       // teardown the context now that we have visited the children
2138       tearDownVisitingContext(context);
2139     }
2140 
2141     return invokedComponent;
2142   }
2143 
2144   /**
2145    * Override to calls the hooks for setting up and tearing down the
2146    * context before the children are visited.
2147    * @see #setupVisitingContext
2148    * @see #tearDownVisitingContext
2149    */
2150   @Override
2151   public boolean invokeOnComponent(
2152     FacesContext context,
2153     String clientId,
2154     ContextCallback callback)
2155     throws FacesException
2156   {
2157     boolean invokedComponent;
2158 
2159     // set up the context for visiting the children
2160     setupVisitingContext(context);
2161 
2162     try
2163     {
2164       String thisClientId = getClientId(context);
2165 
2166       if (clientId.equals(thisClientId))
2167       {
2168         // push component to the stack before invoking the component.
2169         RequestContext requestContext = RequestContext.getCurrentInstance();
2170         requestContext.pushCurrentComponent(context, this);
2171         pushComponentToEL(context, null);
2172 
2173         try
2174         {
2175           // this is the component we want, so invoke the callback
2176           callback.invokeContextCallback(context, this);
2177         }
2178         finally
2179         {
2180           popComponentFromEL(context);
2181           requestContext.popCurrentComponent(context, this);
2182         }
2183 
2184         // we found the component
2185         invokedComponent = true;
2186       }
2187       else
2188       {
2189         // set up the children visiting context to iterate through children. We inline this
2190         // code instead of calling super in order
2191         // to avoid making an extra call to getClientId().
2192         invokedComponent = invokeOnChildrenComponents(context, clientId, callback);
2193       }
2194     }
2195     finally
2196     {
2197       // teardown the context now that we have visited the component
2198       tearDownVisitingContext(context);
2199     }
2200 
2201     return invokedComponent;
2202   }
2203 
2204 
2205   @Override
2206   public void subscribeToEvent(Class<? extends SystemEvent> eventClass, ComponentSystemEventListener componentListener)
2207   {
2208     if (eventClass == null)
2209     {
2210         throw new NullPointerException("eventClass required");
2211     }
2212     if (componentListener == null)
2213     {
2214         throw new NullPointerException("componentListener required");
2215     }
2216 
2217     FacesBean bean = getFacesBean();
2218 
2219     AttachedObjects<Class<? extends SystemEvent>, SystemEventListener> eventStorage =
2220       (AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY);
2221 
2222     if (eventStorage == null)
2223     {
2224       eventStorage = new AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>();
2225       bean.setProperty(_SYSTEM_EVENT_LISTENERS_KEY, eventStorage);
2226     }
2227 
2228     if (componentListener instanceof SystemEventListener && componentListener instanceof StateHolder)
2229       eventStorage.addAttachedObject(eventClass, (SystemEventListener) componentListener);
2230     else
2231       eventStorage.addAttachedObject(eventClass, new ComponentSystemEventListenerWrapper(componentListener, this));
2232   }
2233 
2234   @Override
2235   public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass,
2236                                    ComponentSystemEventListener componentListener)
2237   {
2238     if (eventClass == null)
2239     {
2240         throw new NullPointerException("eventClass required");
2241     }
2242     if (componentListener == null)
2243     {
2244         throw new NullPointerException("componentListener required");
2245     }
2246 
2247     FacesBean bean = getFacesBean();
2248 
2249     AttachedObjects<Class<? extends SystemEvent>, SystemEventListener> eventStorage =
2250       (AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY);
2251 
2252     if (eventStorage == null)
2253     {
2254       return;
2255     }
2256 
2257     if (componentListener instanceof SystemEventListener && componentListener instanceof StateHolder)
2258     {
2259       eventStorage.removeAttachedObject(eventClass, (SystemEventListener) componentListener);
2260     }
2261     else
2262     {
2263       // ComponentSystemEventListenerWrapper implements equals() to compare listener and component
2264       eventStorage.removeAttachedObject(eventClass, new ComponentSystemEventListenerWrapper(componentListener, this));
2265     }
2266   }
2267 
2268   @Override
2269   public List<SystemEventListener> getListenersForEventClass(Class<? extends SystemEvent> eventClass)
2270   {
2271     FacesBean bean = getFacesBean();
2272 
2273     AttachedObjects<Class<? extends SystemEvent>, SystemEventListener> eventStorage =
2274       (AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY);
2275 
2276     if (eventStorage == null)
2277     {
2278       return Collections.emptyList();
2279     }
2280 
2281     return eventStorage.getAttachedObjectList(eventClass);
2282   }
2283 
2284   // ------------------------- Client behavior holder methods -------------------------
2285 
2286   /**
2287    * Utility method to assist sub-classes in the implementation of the
2288    * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface.
2289    * <p>This method must only
2290    * be called by classes that implement the interface, doing otherwise will result in an exception.
2291    * </p>
2292    * @param eventName The event name
2293    * @param behavior The behavior to add
2294    * @see javax.faces.component.behavior.ClientBehaviorHolder#addClientBehavior(String, ClientBehavior)
2295    */
2296   protected void addClientBehavior(
2297     String         eventName,
2298     ClientBehavior behavior)
2299   {
2300     // This will throw a class cast exception when illegally called by a class that does not
2301     // implement ClientBehaviorHolder
2302     Collection<String> events = ((ClientBehaviorHolder)this).getEventNames();
2303 
2304     // This will throw a null pointer exception if the component author did not correctly implement
2305     // the ClientBehaviorHolder contract which requires a non-empty collection to be returned from
2306     // getEventNames
2307     if (!events.contains(eventName))
2308     {
2309       return;
2310     }
2311 
2312     FacesBean bean = getFacesBean();
2313 
2314     AttachedObjects<String, ClientBehavior> behaviors = (
2315             AttachedObjects<String, ClientBehavior>)bean.getProperty(_CLIENT_BEHAVIORS_KEY);
2316 
2317     if (behaviors == null)
2318     {
2319       behaviors = new AttachedObjects<String, ClientBehavior>();
2320       bean.setProperty(_CLIENT_BEHAVIORS_KEY, behaviors);
2321     }
2322 
2323     behaviors.addAttachedObject(eventName, behavior);
2324   }
2325 
2326   // Note, we do not need to provide a default implementation for the event names, as client
2327   // behavior holder components must provide a non-empty list of event names. UIComponentBase
2328   // decided to return a non-valid null in their code, but that is only confusing to the user, it
2329   // is better to not implement the method and force the users to write the method upon interface
2330   // implementation.
2331   //protected Collection<String> getEventNames() {}
2332 
2333   /**
2334    * Utility method to assist sub-classes in the implementation of the
2335    * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface.
2336    * <p>This method must only
2337    * be called by classes that implement the interface, doing otherwise will result in an exception.
2338    * </p>
2339    * @see javax.faces.component.behavior.ClientBehaviorHolder#getClientBehaviors()
2340    * @return Read-only map of the client behaviors for this component
2341    */
2342   protected Map<String, List<ClientBehavior>> getClientBehaviors()
2343   {
2344     AttachedObjects<String, ClientBehavior> behaviors = (
2345             AttachedObjects<String, ClientBehavior>)getFacesBean().getProperty(_CLIENT_BEHAVIORS_KEY);
2346 
2347     if (behaviors == null)
2348     {
2349       return Collections.emptyMap();
2350     }
2351 
2352     return behaviors.getAttachedObjectMap();
2353   }
2354 
2355   /**
2356    * Utility method to assist sub-classes in the implementation of the
2357    * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface.
2358    * <p>This method must only
2359    * be called by classes that implement the interface, doing otherwise will result in an exception.
2360    * </p>
2361    * @return null
2362    * @see javax.faces.component.behavior.ClientBehaviorHolder#getDefaultEventName()
2363    */
2364   protected String getDefaultEventName()
2365   {
2366     _ensureClientBehaviorHolder();
2367     return null;
2368   }
2369 
2370   private void _ensureClientBehaviorHolder()
2371   {
2372     if (!(this instanceof ClientBehaviorHolder))
2373     {
2374       throw new IllegalStateException("Component must implement ClientBehaviorHolder in order " +
2375         "to make use of this method.");
2376     }
2377   }
2378   // ------------------------- End of the client behavior holder methods -------------------------
2379 
2380   /**
2381    * <p>
2382    * This gets a single threadlocal shared stringbuilder instance, each time you call
2383    * __getSharedStringBuilder it sets the length of the stringBuilder instance to 0.
2384    * </p><p>
2385    * This allows you to use the same StringBuilder instance over and over.
2386    * You must call toString on the instance before calling __getSharedStringBuilder again.
2387    * </p>
2388    * Example that works
2389    * <pre><code>
2390    * StringBuilder sb1 = __getSharedStringBuilder();
2391    * sb1.append(a).append(b);
2392    * String c = sb1.toString();
2393    *
2394    * StringBuilder sb2 = __getSharedStringBuilder();
2395    * sb2.append(b).append(a);
2396    * String d = sb2.toString();
2397    * </code></pre>
2398    * <br><br>
2399    * Example that doesn't work, you must call toString on sb1 before
2400    * calling __getSharedStringBuilder again.
2401    * <pre><code>
2402    * StringBuilder sb1 = __getSharedStringBuilder();
2403    * StringBuilder sb2 = __getSharedStringBuilder();
2404    *
2405    * sb1.append(a).append(b);
2406    * String c = sb1.toString();
2407    *
2408    * sb2.append(b).append(a);
2409    * String d = sb2.toString();
2410    * </code></pre>
2411    *
2412    */
2413   static StringBuilder __getSharedStringBuilder()
2414   {
2415     StringBuilder sb = _STRING_BUILDER.get();
2416 
2417     if (sb == null)
2418     {
2419       sb = new StringBuilder();
2420       _STRING_BUILDER.set(sb);
2421     }
2422 
2423     // clear out the stringBuilder by setting the length to 0
2424     sb.setLength(0);
2425 
2426     return sb;
2427   }
2428 
2429   /**
2430    * render a component. this is called by renderers whose
2431    * getRendersChildren() return true.
2432    * @deprecated {@link UIComponent#encodeAll(FacesContext)} should be used instead of this method
2433    */
2434   @Deprecated
2435   void __encodeRecursive(FacesContext context, UIComponent component)
2436     throws IOException
2437   {
2438     component.encodeAll(context);
2439   }
2440 
2441   static private UIComponent _findInsideOf(
2442     UIComponent from,
2443     String id)
2444   {
2445     Iterator<UIComponent> kids = from.getFacetsAndChildren();
2446     while (kids.hasNext())
2447     {
2448       UIComponent kid = kids.next();
2449       if (id.equals(kid.getId()))
2450         return kid;
2451 
2452       if (!(kid instanceof NamingContainer))
2453       {
2454         UIComponent returned = _findInsideOf(kid, id);
2455         if (returned != null)
2456           return returned;
2457       }
2458     }
2459 
2460     return null;
2461   }
2462 
2463   /**
2464    * <p>Verify that the specified component id is safe to add to the tree.
2465    * </p>
2466    *
2467    * @param id The proposed component id to check for validity
2468    *
2469    * @exception IllegalArgumentException if <code>id</code>
2470    *  is <code>null</code> or contains invalid characters
2471    */
2472   private void _validateId(String id)
2473   {
2474     if (id == null)
2475       return;
2476 
2477 
2478     int n = id.length();
2479     if (0 == n ||
2480         NamingContainer.SEPARATOR_CHAR == id.charAt(0))
2481       _throwBadId(id);
2482 
2483     for (int i = 0; i < n; i++)
2484     {
2485       char c = id.charAt(i);
2486       if (i == 0)
2487       {
2488         if (!Character.isLetter(c) && (c != '_'))
2489           _throwBadId(id);
2490       }
2491       else
2492       {
2493         if (!(Character.isLetter(c) ||
2494               Character.isDigit(c) ||
2495               (c == '-') || (c == '_')))
2496         {
2497           _throwBadId(id);
2498         }
2499       }
2500     }
2501   }
2502 
2503   private void _throwBadId(String id)
2504   {
2505     throw new IllegalArgumentException(_LOG.getMessage(
2506       "ILLEGAL_ID", id));
2507   }
2508 
2509   private void _init(
2510     String rendererType)
2511   {
2512     FacesBean oldBean = _facesBean;
2513     FacesBean newBean = createFacesBean(rendererType);;
2514 
2515     if (oldBean != null)
2516       newBean.addAll(oldBean);
2517 
2518     _attributes = new ValueMap(newBean);
2519 
2520     _facesBean = newBean;
2521 
2522     // determine whether it is ok to store the attributes locally.  We cache the result since
2523     // this can be a little involved
2524     boolean usesFacesBeanImpl = false;
2525 
2526     if (newBean instanceof UIXFacesBeanImpl)
2527       usesFacesBeanImpl = true;
2528     else
2529     {
2530       // handle the wrapped case
2531       FacesBean currImpl = newBean;
2532 
2533       while (currImpl instanceof FacesBeanWrapper)
2534       {
2535         currImpl = ((FacesBeanWrapper)currImpl).getWrappedBean();
2536 
2537         if (currImpl instanceof UIXFacesBeanImpl)
2538         {
2539           usesFacesBeanImpl = true;
2540           break;
2541         }
2542       }
2543     }
2544 
2545     _usesFacesBeanImpl = usesFacesBeanImpl;
2546   }
2547 
2548   private FacesBean                _facesBean;
2549   private List<UIComponent>        _children;
2550   private Map<String, Object>      _attributes;
2551   private Map<String, UIComponent> _facets;
2552   private UIComponent              _parent;
2553   private String                   _id;
2554   private String                   _clientId;
2555   private boolean                  _usesFacesBeanImpl;
2556 
2557   // Cached instance of the Renderer for this component.
2558   // The instance will be re-retrieved in encodeBegin()
2559   private transient Renderer _cachedRenderer = _UNDEFINED_RENDERER;
2560   private transient LifecycleRenderer _cachedLifecycleRenderer =
2561                                                 _UNDEFINED_LIFECYCLE_RENDERER;
2562 
2563   // -= Simon Lessard =-
2564   // FIXME: _initialStateMarked is never read
2565   //        So commented out, is that ok? If so, this attribute should be deleted
2566   //private transient boolean _initialStateMarked;
2567 
2568   private static final Iterator<String> _EMPTY_STRING_ITERATOR = CollectionUtils.emptyIterator();
2569   private static final Iterator<UIComponent> _EMPTY_UICOMPONENT_ITERATOR =
2570     CollectionUtils.emptyIterator();
2571 
2572   static private final ThreadLocal<StringBuilder> _STRING_BUILDER =
2573                                                           ThreadLocalUtils.newRequestThreadLocal();
2574 
2575   static private FacesBean.Type _createType()
2576   {
2577     try
2578     {
2579       ClassLoader cl = _getClassLoader();
2580       URL url = cl.getResource("META-INF/faces-bean-type.properties");
2581       if (url != null)
2582       {
2583         Properties properties = new Properties();
2584         InputStream is = url.openStream();
2585         try
2586         {
2587           properties.load(is);
2588           String className = (String)
2589             properties.get(UIXComponentBase.class.getName());
2590           return (FacesBean.Type) cl.loadClass(className).newInstance();
2591         }
2592         finally
2593         {
2594           is.close();
2595         }
2596       }
2597     }
2598     catch (Exception e)
2599     {
2600       _LOG.severe("CANNOT_LOAD_TYPE_PROPERTIES", e);
2601     }
2602 
2603     // For testing purposes, return a valid Type
2604     return new FacesBean.Type();
2605   }
2606 
2607   static private ClassLoader _getClassLoader()
2608   {
2609     ClassLoader loader = Thread.currentThread().getContextClassLoader();
2610     if (loader == null)
2611       loader = FacesBeanFactory.class.getClassLoader();
2612     return loader;
2613   }
2614 
2615   static private class RendererImpl extends Renderer
2616   {
2617   }
2618 
2619   static private class ExtendedRendererImpl extends ExtendedRenderer
2620   {
2621   }
2622 
2623   private static boolean _isClientIdCachingEnabled()
2624   {
2625     return (_CLIENT_ID_CACHING != ClientIdCaching.OFF);
2626   }
2627 
2628   private static boolean _isClientIdDebuggingEnabled()
2629   {
2630     return (_CLIENT_ID_CACHING == ClientIdCaching.DEBUG);
2631   }
2632 
2633   // Warn if caching is disabled + production environment since this is
2634   // undesirable from a performance perspective.
2635   private static void _warnClientIdCachingConfig(FacesContext context)
2636   {
2637     if (_CLIENT_ID_CACHING != ClientIdCaching.ON &&
2638          context.isProjectStage(ProjectStage.Production) &&
2639          !_warnedClientIdCachingConfig(context))
2640     {
2641       _LOG.warning(
2642         "The org.apache.myfaces.trinidad.CLIENT_ID_CACHING system property is set to: " +
2643          _CLIENT_ID_CACHING +
2644         ".  For best performance, client id caching should be ON in production environments.");
2645 
2646       _clientIdCachingConfigWarned(context);
2647     }
2648   }
2649 
2650   // Tests whether we have already warned about the caching config.
2651   // We only want to warn once per run.
2652   private static boolean _warnedClientIdCachingConfig(FacesContext context)
2653   {
2654     ExternalContext external = context.getExternalContext();
2655     Map<String, Object> appMap = external.getApplicationMap();
2656 
2657     return Boolean.TRUE.equals(appMap.get(_WARNED_CLIENT_ID_CACHING_KEY));
2658   }
2659 
2660   // Marks the fact that we have now warned about the caching config.
2661   private static void _clientIdCachingConfigWarned(FacesContext context)
2662   {
2663     ExternalContext external = context.getExternalContext();
2664     Map<String, Object> appMap = external.getApplicationMap();
2665 
2666     appMap.put(_WARNED_CLIENT_ID_CACHING_KEY, Boolean.TRUE);
2667   }
2668 
2669   // Utility for deriving initial CLIENT_ID_CACHING value.
2670   private static ClientIdCaching _initClientIdCaching()
2671   {
2672     String cachingProperty = _getClientIdCachingSystemProperty();
2673     ClientIdCaching caching = _toClientIdCachingEnum(cachingProperty);
2674 
2675     _LOG.config("Client id caching configuration: " + caching);
2676 
2677     return caching;
2678   }
2679 
2680   private static String _getClientIdCachingSystemProperty()
2681   {
2682     try
2683     {
2684       return System.getProperty(_SYSTEM_PROP_CLIENT_ID_CACHING);
2685     }
2686     catch (Throwable t)
2687     {
2688       _LOG.warning(t);
2689     }
2690 
2691     return null;
2692   }
2693 
2694   private static ClientIdCaching _toClientIdCachingEnum(String cachingProperty)
2695   {
2696     try
2697     {
2698       return Enum.valueOf(ClientIdCaching.class,
2699                            (cachingProperty == null) ?
2700                              "ON" :
2701                              cachingProperty.toUpperCase());
2702     }
2703     catch (IllegalArgumentException e)
2704     {
2705       _LOG.warning("Invalid value specified for " +
2706                    _SYSTEM_PROP_CLIENT_ID_CACHING +
2707                    " system property: " +
2708                    cachingProperty +
2709                    ". Valid values are: on | off | debug.");
2710 
2711     }
2712 
2713     return ClientIdCaching.ON;
2714   }
2715 
2716   // Little enum for tracking the client id caching behavior.
2717   private enum ClientIdCaching
2718   {
2719     ON,
2720     OFF,
2721     DEBUG
2722   }
2723 
2724 
2725   public static class ComponentSystemEventListenerWrapper implements SystemEventListener, StateHolder, ComponentSystemEventListener
2726   {
2727     ComponentSystemEventListenerWrapper(ComponentSystemEventListener listener, UIComponent component)
2728     {
2729       _delegate = listener;
2730       _componentClass = component.getClass();
2731     }
2732 
2733     // Default constructor for state restoration
2734     public ComponentSystemEventListenerWrapper()
2735     {
2736     }
2737 
2738     @Override
2739     public boolean equals(Object o)
2740     {
2741       if (o == this)
2742       {
2743         return true;
2744       }
2745       else if (o instanceof ComponentSystemEventListenerWrapper)
2746       {
2747         ComponentSystemEventListenerWrapper other = (ComponentSystemEventListenerWrapper) o;
2748         return _componentClass.equals(other._componentClass) && _delegate.equals(other._delegate);
2749       }
2750 
2751       return false;
2752     }
2753 
2754     @Override
2755     public int hashCode()
2756     {
2757       return _componentClass.hashCode() + _delegate.hashCode();
2758     }
2759 
2760     // SystemEventListener implementation
2761 
2762     @Override
2763     public void processEvent(SystemEvent event) throws AbortProcessingException
2764     {
2765       assert (event instanceof ComponentSystemEvent);
2766       processEvent((ComponentSystemEvent) event);
2767     }
2768 
2769     @Override
2770     public void processEvent(ComponentSystemEvent event)
2771     {
2772       _delegate.processEvent((ComponentSystemEvent) event);
2773     }
2774 
2775     @Override
2776     public boolean isListenerForSource(Object source)
2777     {
2778       if (_delegate instanceof SystemEventListener)
2779       {
2780         return ((SystemEventListener)_delegate).isListenerForSource(source);
2781       }
2782 
2783       // From the spec: and its implementation of SystemEventListener.isListenerForSource(java.lang.Object) must return true
2784       // if the instance class of this UIComponent is assignable from the argument to isListenerForSource.
2785       return _componentClass.isAssignableFrom(source.getClass());
2786     }
2787 
2788     // StateHolder Implementation
2789 
2790     @Override
2791     public Object saveState(FacesContext context)
2792     {
2793       if (_delegate instanceof UIComponent)
2794       {
2795         return null;
2796       }
2797 
2798       Object[] state = new Object[2];
2799       state[0] = StateUtils.saveStateHolder(context, _delegate);
2800       state[1] = _componentClass;
2801 
2802       return state;
2803     }
2804 
2805     @Override
2806     public void restoreState(FacesContext context, Object state)
2807     {
2808       if (state == null)
2809       {
2810         return;
2811       }
2812 
2813       Object[] stateArr = (Object[]) state;
2814       Object saved = stateArr[0];
2815 
2816       _delegate = (ComponentSystemEventListener) ((saved == null) ? UIComponent .getCurrentComponent(context)
2817                                                 : StateUtils.restoreStateHolder(context, saved));
2818       _componentClass = (Class<?>)stateArr[1];
2819     }
2820 
2821     @Override
2822     public boolean isTransient()
2823     {
2824       if (_delegate instanceof StateHolder)
2825       {
2826           return ((StateHolder)_delegate).isTransient();
2827       }
2828       return false;
2829     }
2830 
2831     @Override
2832     public void setTransient(boolean isTransient)
2833     {
2834       if (_delegate instanceof StateHolder)
2835       {
2836         ((StateHolder)_delegate).setTransient(isTransient);
2837       }
2838     }
2839 
2840 
2841     private ComponentSystemEventListener _delegate;
2842     private Class<?> _componentClass;
2843   }
2844 
2845   private static final ClientIdCaching _CLIENT_ID_CACHING = _initClientIdCaching();
2846 
2847   // System property controlling whether client ID caching is enabled
2848   private static final String _SYSTEM_PROP_CLIENT_ID_CACHING =
2849     "org.apache.myfaces.trinidad.CLIENT_ID_CACHING";
2850 
2851   // Application map key indicating that we've already warned once
2852   // that the client id caching configuration is not optimized for
2853   // production mode.
2854   private static final String _WARNED_CLIENT_ID_CACHING_KEY =
2855     "org.apache.myfaces.trinidad.WARNED_CLIENT_ID_CACHING";
2856 
2857   static private final LifecycleRenderer _UNDEFINED_LIFECYCLE_RENDERER =
2858                                                 new ExtendedRendererImpl();
2859   static private final Renderer _UNDEFINED_RENDERER = new RendererImpl();
2860 }