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