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 javax.faces.component;
20  
21  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
22  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
23  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
24  
25  import javax.el.ValueExpression;
26  import javax.faces.FacesException;
27  import javax.faces.component.behavior.Behavior;
28  import javax.faces.component.behavior.ClientBehavior;
29  import javax.faces.component.visit.VisitCallback;
30  import javax.faces.component.visit.VisitContext;
31  import javax.faces.context.FacesContext;
32  import javax.faces.el.ValueBinding;
33  import javax.faces.event.AbortProcessingException;
34  import javax.faces.event.BehaviorEvent;
35  import javax.faces.event.FacesEvent;
36  import javax.faces.event.FacesListener;
37  import javax.faces.event.PostAddToViewEvent;
38  import javax.faces.event.PostValidateEvent;
39  import javax.faces.event.PreRemoveFromViewEvent;
40  import javax.faces.event.PreRenderComponentEvent;
41  import javax.faces.event.PreValidateEvent;
42  import javax.faces.event.SystemEvent;
43  import javax.faces.event.SystemEventListener;
44  import javax.faces.render.RenderKit;
45  import javax.faces.render.Renderer;
46  import javax.faces.view.Location;
47  import java.io.IOException;
48  import java.io.Serializable;
49  import java.lang.reflect.Array;
50  import java.util.ArrayList;
51  import java.util.Collection;
52  import java.util.Collections;
53  import java.util.HashMap;
54  import java.util.Iterator;
55  import java.util.List;
56  import java.util.Map;
57  import java.util.logging.Level;
58  import java.util.logging.Logger;
59  
60  
61  /**
62   * TODO: IMPLEMENT HERE - Delta state saving support
63   * 
64   * Standard implementation of the UIComponent base class; all standard JSF components extend this class.
65   * <p>
66   * <i>Disclaimer</i>: The official definition for the behaviour of this class is the JSF 1.1 specification but for legal
67   * reasons the specification cannot be replicated here. Any javadoc here therefore describes the current implementation
68   * rather than the spec, though this class has been verified as correctly implementing the spec.
69   * 
70   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a> for
71   * more.
72   * 
73   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
74   * @version $Revision: 1303056 $ $Date: 2012-03-20 12:58:27 -0500 (Tue, 20 Mar 2012) $
75   */
76  @JSFComponent(type = "javax.faces.ComponentBase", family = "javax.faces.ComponentBase",
77                desc = "base component when all components must inherit",
78                tagClass = "javax.faces.webapp.UIComponentELTag", configExcluded = true)
79  @JSFJspProperty(name = "binding", returnType = "javax.faces.component.UIComponent",
80                  longDesc = "Identifies a backing bean property (of type UIComponent or appropriate subclass) to bind "
81                             + "to this component instance. This value must be an EL expression.",
82                  desc = "backing bean property to bind to this component instance")
83  public abstract class UIComponentBase extends UIComponent
84  {
85      //private static Log log = LogFactory.getLog(UIComponentBase.class);
86      private static Logger log = Logger.getLogger(UIComponentBase.class.getName());
87  
88      private static final Iterator<UIComponent> _EMPTY_UICOMPONENT_ITERATOR = new _EmptyIterator<UIComponent>();
89  
90      private static final String _STRING_BUILDER_KEY
91              = "javax.faces.component.UIComponentBase.SHARED_STRING_BUILDER";
92  
93      private _ComponentAttributesMap _attributesMap = null;
94      private List<UIComponent> _childrenList = null;
95      private Map<String, UIComponent> _facetMap = null;
96      private _DeltaList<FacesListener> _facesListeners = null;
97      private String _clientId = null;
98      private String _id = null;
99      private UIComponent _parent = null;
100     private boolean _transient = false;
101     
102     private boolean _isRendererTypeSet = false;
103     private String _rendererType;
104     private String _markCreated;
105     private String _facetName;
106 
107     /**
108      * This map holds ClientBehavior instances.
109      * 
110      *  Note that BehaviorBase implements PartialStateHolder, so this class 
111      *  should deal with that fact on clearInitialState() and 
112      *  markInitialState() methods.
113      * 
114      *  Also, the map used by this instance is not set from outside this class.
115      *  
116      *  Note it is possible (but maybe not expected/valid) to manipulate 
117      *  the values of the map(the list) but not put instances on the map 
118      *  directly, because ClientBehaviorHolder.getClientBehaviors says that 
119      *  this method should return a non null unmodificable map.
120      *  
121      */
122     private Map<String, List<ClientBehavior>> _behaviorsMap = null;
123     private transient Map<String, List<ClientBehavior>> _unmodifiableBehaviorsMap = null;
124     
125     private transient FacesContext _facesContext;
126     private transient Boolean _cachedIsRendered;
127     private transient Renderer _cachedRenderer;
128     
129     public UIComponentBase()
130     {
131     }
132 
133     /**
134      * Put the provided value-binding into a map of value-bindings associated with this component.
135      * 
136      * @deprecated Replaced by setValueExpression
137      */
138     @Override
139     public void setValueBinding(String name, ValueBinding binding)
140     {
141         setValueExpression(name, binding == null ? null : new _ValueBindingToValueExpression(binding));
142     }
143 
144     /**
145      * Set an identifier for this component which is unique within the scope of the nearest ancestor NamingContainer
146      * component. The id is not necessarily unique across all components in the current view.
147      * <p>
148      * The id must start with an underscore if it is generated by the JSF framework, and must <i>not</i> start with an
149      * underscore if it has been specified by the user (eg in a JSP tag).
150      * <p>
151      * The first character of the id must be an underscore or letter. Following characters may be letters, digits,
152      * underscores or dashes.
153      * <p>
154      * Null is allowed as a parameter, and will reset the id to null.
155      * <p>
156      * The clientId of this component is reset by this method; see getClientId for more info.
157      * 
158      * @throws IllegalArgumentException
159      *             if the id is not valid.
160      */
161     @Override
162     public void setId(String id)
163     {
164         isIdValid(id);
165         _id = id;
166         _clientId = null;
167     }
168 
169     /**
170      * <p>Set the parent <code>UIComponent</code> of this
171      * <code>UIComponent</code>.</p>
172      * 
173      * @param parent The new parent, or <code>null</code> for the root node
174      *  of a component tree
175      */
176     @Override
177     public void setParent(UIComponent parent)
178     {
179         // removing kids OR this is UIViewRoot
180         if (parent == null)
181         {
182             // not UIViewRoot...
183             if (_parent != null && _parent.isInView())
184             {
185                 // trigger the "remove event" lifecycle
186                 // and call setInView(false) for all children/facets
187                 // doing this => recursive
188                 FacesContext facesContext = getFacesContext();
189                 if (facesContext.isProcessingEvents())
190                 {
191                     _publishPreRemoveFromViewEvent(facesContext, this);
192                 }
193                 else
194                 {
195                     _updateInView(this, false);
196                 }
197             }
198             _parent = parent;
199         }
200         else
201         {
202             _parent = parent;
203             if (parent.isInView())
204             {
205                 // trigger the ADD_EVENT and call setInView(true)
206                 // recursive for all kids/facets...
207                 // Application.publishEvent(java.lang.Class, java.lang.Object)  must be called, passing 
208                 // PostAddToViewEvent.class as the first argument and the newly added component as the second 
209                 // argument.
210                 FacesContext facesContext = getFacesContext();
211                 if (facesContext.isProcessingEvents())
212                 {
213                     _publishPostAddToViewEvent(facesContext, this);
214                 }
215                 else
216                 {
217                     _updateInView(this, true);
218                 }
219             }
220         }
221     }
222 
223     
224     /**
225      * Publish PostAddToViewEvent to the component and all facets and children.
226      * 
227      * @param context
228      * @param component
229      */
230     private static void _publishPostAddToViewEvent(FacesContext context, UIComponent component)
231     {
232         component.setInView(true);
233         context.getApplication().publishEvent(context, PostAddToViewEvent.class, component.getClass(), component);
234         
235         if (component.getChildCount() > 0)
236         {
237             // PostAddToViewEvent could cause component relocation
238             // (h:outputScript, h:outputStylesheet, composite:insertChildren, composite:insertFacet)
239             // so we need to check if the component was relocated or not
240           
241             List<UIComponent> children = component.getChildren();
242             for (int i = 0; i < children.size(); i++)
243             {
244                 // spin on same index while component removed/replaced
245                 // to prevent skipping components:
246                 while (true)
247                 {
248                     UIComponent child = children.get(i);
249                     child.pushComponentToEL(context, child);
250                     try
251                     {
252                         _publishPostAddToViewEvent(context, child);
253                     }
254                     finally
255                     {
256                         child.popComponentFromEL(context);
257                     }
258                     if (i < children.size() && children.get(i) != child)
259                     {
260                         continue;
261                     }
262                     break;
263                 }
264             }
265         }
266         if (component.getFacetCount() > 0)
267         {
268             for (UIComponent child : component.getFacets().values())
269             {
270                 child.pushComponentToEL(context, child);
271                 try
272                 {
273                     _publishPostAddToViewEvent(context, child);
274                 }
275                 finally
276                 {
277                     child.popComponentFromEL(context);
278                 }
279             }
280         }        
281     }
282     
283     /**
284      * Publish PreRemoveFromViewEvent to the component and all facets and children.
285      * 
286      * @param context
287      * @param component
288      */
289     private static void _publishPreRemoveFromViewEvent(FacesContext context, UIComponent component)
290     {
291         component.setInView(false);
292         context.getApplication().publishEvent(context, PreRemoveFromViewEvent.class, component.getClass(), component);
293         
294         if (component.getChildCount() > 0)
295         {
296             for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
297             {
298                 UIComponent child = component.getChildren().get(i);
299                 _publishPreRemoveFromViewEvent(context, child);
300             }
301         }
302         if (component.getFacetCount() > 0)
303         {
304             for (UIComponent child : component.getFacets().values())
305             {
306                 _publishPreRemoveFromViewEvent(context, child);
307             }
308         }        
309     }    
310     
311     private static void _updateInView(UIComponent component, boolean isInView)
312     {
313         component.setInView(isInView);
314         
315         if (component.getChildCount() > 0)
316         {
317             for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
318             {
319                 UIComponent child = component.getChildren().get(i);
320                 _updateInView(child, isInView);
321             }
322         }
323         if (component.getFacetCount() > 0)
324         {
325             for (UIComponent child : component.getFacets().values())
326             {
327                 _updateInView(child, isInView);
328             }
329         }        
330     }  
331     
332     /**
333      * 
334      * @param eventName
335      * @param behavior
336      * 
337      * @since 2.0
338      */
339     public void addClientBehavior(String eventName, ClientBehavior behavior)
340     {
341         Collection<String> eventNames = getEventNames();
342         
343         if(eventNames == null)
344         {
345             //component didn't implement getEventNames properly
346             //log an error and return
347             if(log.isLoggable(Level.SEVERE))
348             {
349                 log.severe("attempted to add a behavior to a component which did not properly "
350                            + "implement getEventNames.  getEventNames must not return null");
351                 return;
352             }
353         }
354         
355         if(eventNames.contains(eventName))
356         {
357             if(_behaviorsMap == null)
358             {
359                 _behaviorsMap = new HashMap<String,List<ClientBehavior>>();
360             }
361             
362             List<ClientBehavior> behaviorsForEvent = _behaviorsMap.get(eventName);
363             if(behaviorsForEvent == null)
364             {
365                 // Normally have client only 1 client behaviour per event name,
366                 // so size 2 must be sufficient: 
367                 behaviorsForEvent = new _DeltaList<ClientBehavior>(new ArrayList<ClientBehavior>(2));
368                 _behaviorsMap.put(eventName, behaviorsForEvent);
369             }
370             
371             behaviorsForEvent.add(behavior);
372             _unmodifiableBehaviorsMap = null;
373         }
374     }
375 
376     /**
377      * Invoke any listeners attached to this object which are listening for an event whose type matches the specified
378      * event's runtime type.
379      * <p>
380      * This method does not propagate the event up to parent components, ie listeners attached to parent components
381      * don't automatically get called.
382      * <p>
383      * If any of the listeners throws AbortProcessingException then that exception will prevent any further listener
384      * callbacks from occurring, and the exception propagates out of this method without alteration.
385      * <p>
386      * ActionEvent events are typically queued by the renderer associated with this component in its decode method;
387      * ValueChangeEvent events by the component's validate method. In either case the event's source property references
388      * a component. At some later time the UIViewRoot component iterates over its queued events and invokes the
389      * broadcast method on each event's source object.
390      * 
391      * @param event
392      *            must not be null.
393      */
394     @Override
395     public void broadcast(FacesEvent event) throws AbortProcessingException
396     {
397         if (event == null)
398         {
399             throw new NullPointerException("event");
400         }
401         try
402         {
403             if (event instanceof BehaviorEvent && event.getComponent() == this)
404             {
405                 Behavior behavior = ((BehaviorEvent) event).getBehavior();
406                 behavior.broadcast((BehaviorEvent) event);
407             }
408             
409             if (_facesListeners == null)
410             {
411                 return;
412             }
413             // perf: _facesListeners is RandomAccess instance (javax.faces.component._DeltaList)
414             for (int i = 0, size = _facesListeners.size(); i < size; i++)
415             {
416                 FacesListener facesListener = _facesListeners.get(i);
417                 if (event.isAppropriateListener(facesListener))
418                 {
419                     event.processListener(facesListener);
420                 }
421             }
422         }
423         catch (Exception ex)
424         {
425             if (ex instanceof AbortProcessingException)
426             {
427                 throw (AbortProcessingException) ex;
428             }
429             String location = getComponentLocation(this);
430             throw new FacesException("Exception while calling broadcast on component : " 
431                     + getPathToComponent(this) 
432                     + (location != null ? " created from: " + location : ""), ex);
433         }
434     }
435     
436     public void clearInitialState()
437     {
438         super.clearInitialState();
439         if (_facesListeners != null)
440         {
441             _facesListeners.clearInitialState();
442         }
443         if (_behaviorsMap != null)
444         {
445             for (Map.Entry<String, List<ClientBehavior> > entry : _behaviorsMap.entrySet())
446             {
447                 ((PartialStateHolder) entry.getValue()).clearInitialState();
448             }
449         }
450         if (_systemEventListenerClassMap != null)
451         {
452             for (Map.Entry<Class<? extends SystemEvent>, List<SystemEventListener>> entry : 
453                 _systemEventListenerClassMap.entrySet())
454             {
455                 ((PartialStateHolder) entry.getValue()).clearInitialState();
456             }
457         }
458         _isRendererTypeSet = false;
459     }
460 
461     /**
462      * Check the submitted form parameters for data associated with this component. This default implementation
463      * delegates to this component's renderer if there is one, and otherwise ignores the call.
464      */
465     @Override
466     public void decode(FacesContext context)
467     {
468         if (context == null)
469         {
470             throw new NullPointerException("context");
471         }
472         
473         setCachedRenderer(null);
474         Renderer renderer = getRenderer(context);
475         if (renderer != null)
476         {
477             setCachedRenderer(renderer);
478             try
479             {
480                 renderer.decode(context, this);
481             }
482             finally
483             {
484                 setCachedRenderer(null);
485             }
486         }
487 
488     }
489 
490     public void encodeAll(FacesContext context) throws IOException
491     {
492         if (context == null)
493         {
494             throw new NullPointerException();
495         }
496 
497         pushComponentToEL(context, this);
498         try
499         {
500             setCachedIsRendered(null);
501             boolean rendered = isRendered(); 
502             setCachedIsRendered(rendered);
503             if (!rendered)
504             {
505                 setCachedIsRendered(null);
506                 return;
507             }
508             setCachedRenderer(null);
509             setCachedRenderer(getRenderer(context));
510         }
511         finally
512         {
513             popComponentFromEL(context);
514         }
515 
516         try
517         {
518             //if (isRendered()) {
519             this.encodeBegin(context);
520 
521             // rendering children
522             if (this.getRendersChildren())
523             {
524                 this.encodeChildren(context);
525             } // let children render itself
526             else
527             {
528                 if (this.getChildCount() > 0)
529                 {
530                     for (int i = 0; i < this.getChildCount(); i++)
531                     {
532                         UIComponent comp = this.getChildren().get(i);
533                         comp.encodeAll(context);
534                     }
535                 }
536             }
537             this.encodeEnd(context);
538             //}
539         }
540         finally
541         {
542             setCachedIsRendered(null);
543             setCachedRenderer(null);
544         }
545     }
546 
547     @Override
548     public void encodeBegin(FacesContext context) throws IOException
549     {
550         if (context == null)
551         {
552             throw new NullPointerException("context");
553         }
554 
555         try
556         {
557             setCachedFacesContext(context);
558             // Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
559             pushComponentToEL(context, this);
560     
561             if (isRendered())
562             {
563                 // If our rendered property is true, render the beginning of the current state of this
564                 // UIComponent to the response contained in the specified FacesContext.
565     
566                 // Call Application.publishEvent(java.lang.Class, java.lang.Object), passing BeforeRenderEvent.class as
567                 // the first argument and the component instance to be rendered as the second argument.
568     
569                 // The main issue we have here is that the listeners are normally just registered
570                 // to UIComponent, how do we deal with inherited ones?
571                 // We have to ask the EG
572                 context.getApplication().publishEvent(context,  PreRenderComponentEvent.class, UIComponent.class, this);
573     
574                 Renderer renderer = getRenderer(context);
575                 if (renderer != null)
576                 {
577                     // If a Renderer is associated with this UIComponent, the actual encoding will be delegated to
578                     // Renderer.encodeBegin(FacesContext, UIComponent).
579                     renderer.encodeBegin(context, this);
580                 }
581             }
582         }
583         finally
584         {
585             setCachedFacesContext(null);
586         }
587     }
588 
589     @Override
590     public void encodeChildren(FacesContext context) throws IOException
591     {
592         if (context == null)
593         {
594             throw new NullPointerException("context");
595         }
596 
597         boolean isCachedFacesContext = isCachedFacesContext();
598         try
599         {
600             if (!isCachedFacesContext)
601             {
602                 setCachedFacesContext(context);
603             }
604             if (isRendered())
605             {
606                 // If our rendered property is true, render the child UIComponents of this UIComponent.
607     
608                 Renderer renderer = getRenderer(context);
609                 if (renderer == null)
610                 {
611                     // If no Renderer is associated with this UIComponent, iterate over each of the children of this
612                     // component and call UIComponent.encodeAll(javax.faces.context.FacesContext).
613                     if (getChildCount() > 0)
614                     {
615                         for (int i = 0, childCount = getChildCount(); i < childCount; i++)
616                         {
617                             UIComponent child = getChildren().get(i);
618                             child.encodeAll(context);
619                         }
620                     }
621                 }
622                 else
623                 {
624                     // If a Renderer is associated with this UIComponent, the actual encoding will be delegated to
625                     // Renderer.encodeChildren(FacesContext, UIComponent).
626                     renderer.encodeChildren(context, this);
627                 }
628             }
629         }
630         finally
631         {
632             if (!isCachedFacesContext)
633             {
634                 setCachedFacesContext(null);
635             }
636         }
637     }
638 
639     @Override
640     public void encodeEnd(FacesContext context) throws IOException
641     {
642         try
643         {
644             if (context == null)
645             {
646                 throw new NullPointerException("context");
647             }
648             setCachedFacesContext(context);
649             if (isRendered())
650             {
651                 // If our rendered property is true, render the ending of the current state of this UIComponent.
652                 Renderer renderer = getRenderer(context);
653                 if (renderer != null)
654                 {
655                     // If a Renderer is associated with this UIComponent, the actual encoding will be delegated to
656                     // Renderer.encodeEnd(FacesContext, UIComponent).
657                     renderer.encodeEnd(context, this);
658                 }
659             }
660         }
661         finally
662         {
663             // Call UIComponent.popComponentFromEL(javax.faces.context.FacesContext). before returning regardless
664             // of the value of the rendered property.
665             popComponentFromEL(context);
666             setCachedFacesContext(null);
667         }
668     }
669     
670     /**
671      * Standard method for finding other components by id, inherited by most UIComponent objects.
672      * <p>
673      * The lookup is performed in a manner similar to finding a file in a filesystem; there is a "base" at which to
674      * start, and the id can be for something in the "local directory", or can include a relative path. Here,
675      * NamingContainer components fill the role of directories, and ":" is the "path separator". Note, however, that
676      * although components have a strict parent/child hierarchy, component ids are only prefixed ("namespaced") with the
677      * id of their parent when the parent is a NamingContainer.
678      * <p>
679      * The base node at which the search starts is determined as follows:
680      * <ul>
681      * <li>When expr starts with ':', the search starts with the root component of the tree that this component is in
682      * (ie the ancestor whose parent is null).
683      * <li>Otherwise, if this component is a NamingContainer then the search starts with this component.
684      * <li>Otherwise, the search starts from the nearest ancestor NamingContainer (or the root component if there is no
685      * NamingContainer ancestor).
686      * </ul>
687      * 
688      * @param expr
689      *            is of form "id1:id2:id3".
690      * @return UIComponent or null if no component with the specified id is found.
691      */
692 
693     @Override
694     public UIComponent findComponent(String expr)
695     {
696         if (expr == null)
697         {
698             throw new NullPointerException("expr");
699         }
700         if (expr.length() == 0)
701         {
702             return null;
703         }
704 
705         char separatorChar = UINamingContainer.getSeparatorChar(getFacesContext());
706         UIComponent findBase;
707         if (expr.charAt(0) == separatorChar)
708         {
709             findBase = _ComponentUtils.getRootComponent(this);
710             expr = expr.substring(1);
711         }
712         else
713         {
714             if (this instanceof NamingContainer)
715             {
716                 findBase = this;
717             }
718             else
719             {
720                 findBase = _ComponentUtils.findParentNamingContainer(this, true /* root if not found */);
721             }
722         }
723 
724         int separator = expr.indexOf(separatorChar);
725         if (separator == -1)
726         {
727             return _ComponentUtils.findComponent(findBase, expr, separatorChar);
728         }
729 
730         String id = expr.substring(0, separator);
731         findBase = _ComponentUtils.findComponent(findBase, id, separatorChar);
732         if (findBase == null)
733         {
734             return null;
735         }
736 
737         if (!(findBase instanceof NamingContainer))
738         {
739             throw new IllegalArgumentException("Intermediate identifier " + id + " in search expression " + expr
740                     + " identifies a UIComponent that is not a NamingContainer");
741         }
742 
743         return findBase.findComponent(expr.substring(separator + 1));
744 
745     }
746 
747     /**
748      * Get a map through which all the UIComponent's properties, value-bindings and non-property attributes can be read
749      * and written.
750      * <p>
751      * When writing to the returned map:
752      * <ul>
753      * <li>If this component has an explicit property for the specified key then the setter method is called. An
754      * IllegalArgumentException is thrown if the property is read-only. If the property is readable then the old value
755      * is returned, otherwise null is returned.
756      * <li>Otherwise the key/value pair is stored in a map associated with the component.
757      * </ul>
758      * Note that value-bindings are <i>not</i> written by put calls to this map. Writing to the attributes map using a
759      * key for which a value-binding exists will just store the value in the attributes map rather than evaluating the
760      * binding, effectively "hiding" the value-binding from later attributes.get calls. Setter methods on components
761      * commonly do <i>not</i> evaluate a binding of the same name; they just store the provided value directly on the
762      * component.
763      * <p>
764      * When reading from the returned map:
765      * <ul>
766      * <li>If this component has an explicit property for the specified key then the getter method is called. If the
767      * property exists, but is read-only (ie only a setter method is defined) then an IllegalArgumentException is
768      * thrown.
769      * <li>If the attribute map associated with the component has an entry with the specified key, then that is
770      * returned.
771      * <li>If this component has a value-binding for the specified key, then the value-binding is evaluated to fetch the
772      * value.
773      * <li>Otherwise, null is returned.
774      * </ul>
775      * Note that components commonly define getter methods such that they evaluate a value-binding of the same name if
776      * there isn't yet a local property.
777      * <p>
778      * Assigning values to the map which are not explicit properties on the underlying component can be used to "tunnel"
779      * attributes from the JSP tag (or view-specific equivalent) to the associated renderer without modifying the
780      * component itself.
781      * <p>
782      * Any value-bindings and non-property attributes stored in this map are automatically serialized along with the
783      * component when the view is serialized.
784      */
785     @Override
786     public Map<String, Object> getAttributes()
787     {
788         if (_attributesMap == null)
789         {
790             _attributesMap = new _ComponentAttributesMap(this);
791         }
792 
793         return _attributesMap;
794     }
795 
796     /**
797      * Return the number of direct child components this component has.
798      * <p>
799      * Identical to getChildren().size() except that when this component has no children this method will not force an
800      * empty list to be created.
801      */
802     @Override
803     public int getChildCount()
804     {
805         return _childrenList == null ? 0 : _childrenList.size();
806     }
807 
808     /**
809      * Return a list of the UIComponent objects which are direct children of this component.
810      * <p>
811      * The list object returned has some non-standard behaviour:
812      * <ul>
813      * <li>The list is type-checked; only UIComponent objects can be added.
814      * <li>If a component is added to the list with an id which is the same as some other component in the list then an
815      * exception is thrown. However multiple components with a null id may be added.
816      * <li>The component's parent property is set to this component. If the component already had a parent, then the
817      * component is first removed from its original parent's child list.
818      * </ul>
819      */
820     @Override
821     public List<UIComponent> getChildren()
822     {
823         if (_childrenList == null)
824         {
825             _childrenList = new _ComponentChildrenList(this);
826         }
827         return _childrenList;
828     }
829     
830     /**
831      * 
832      * @return
833      * 
834      * @since 2.0
835      */
836     public Map<String,List<ClientBehavior>> getClientBehaviors()
837     {
838         if(_behaviorsMap == null)
839         {
840             return Collections.emptyMap();
841         }
842 
843         return wrapBehaviorsMap();
844     }
845 
846     /**
847      * Get a string which can be output to the response which uniquely identifies this UIComponent within the current
848      * view.
849      * <p>
850      * The component should have an id attribute already assigned to it; however if the id property is currently null
851      * then a unique id is generated and set for this component. This only happens when components are programmatically
852      * created without ids, as components created by a ViewHandler should be assigned ids when they are created.
853      * <p>
854      * If this component is a descendant of a NamingContainer then the client id is of form
855      * "{namingContainerId}:{componentId}". Note that the naming container's id may itself be of compound form if it has
856      * an ancestor naming container. Note also that this only applies to naming containers; other UIComponent types in
857      * the component's ancestry do not affect the clientId.
858      * <p>
859      * Finally the renderer associated with this component is asked to convert the id into a suitable form. This allows
860      * escaping of any characters in the clientId which are significant for the markup language generated by that
861      * renderer.
862      */
863     @Override
864     public String getClientId(FacesContext context)
865     {
866         if (context == null)
867         {
868             throw new NullPointerException("context");
869         }
870 
871         if (_clientId != null)
872         {
873             return _clientId;
874         }
875 
876         //boolean idWasNull = false;
877         String id = getId();
878         if (id == null)
879         {
880             // Although this is an error prone side effect, we automatically create a new id
881             // just to be compatible to the RI
882             
883             // The documentation of UniqueIdVendor says that this interface should be implemented by
884             // components that also implements NamingContainer. The only component that does not implement
885             // NamingContainer but UniqueIdVendor is UIViewRoot. Anyway we just can't be 100% sure about this
886             // fact, so it is better to scan for the closest UniqueIdVendor. If it is not found use 
887             // viewRoot.createUniqueId, otherwise use UniqueIdVendor.createUniqueId(context,seed).
888             UniqueIdVendor parentUniqueIdVendor = _ComponentUtils.findParentUniqueIdVendor(this);
889             if (parentUniqueIdVendor == null)
890             {
891                 UIViewRoot viewRoot = context.getViewRoot();
892                 if (viewRoot != null)
893                 {
894                     id = viewRoot.createUniqueId();
895                 }
896                 else
897                 {
898                     // The RI throws a NPE
899                     String location = getComponentLocation(this);
900                     throw new FacesException("Cannot create clientId. No id is assigned for component"
901                             + " to create an id and UIViewRoot is not defined: "
902                             + getPathToComponent(this)
903                             + (location != null ? " created from: " + location : ""));
904                 }
905             }
906             else
907             {
908                 id = parentUniqueIdVendor.createUniqueId(context, null);
909             }
910             setId(id);
911             // We remember that the id was null and log a warning down below
912             // idWasNull = true;
913         }
914 
915         UIComponent namingContainer = _ComponentUtils.findParentNamingContainer(this, false);
916         if (namingContainer != null)
917         {
918             String containerClientId = namingContainer.getContainerClientId(context);
919             if (containerClientId != null)
920             {
921                 StringBuilder bld = __getSharedStringBuilder(context);
922                 _clientId = bld.append(containerClientId).append(
923                                       UINamingContainer.getSeparatorChar(context)).append(id).toString();
924             }
925             else
926             {
927                 _clientId = id;
928             }
929         }
930         else
931         {
932             _clientId = id;
933         }
934 
935         Renderer renderer = getRenderer(context);
936         if (renderer != null)
937         {
938             _clientId = renderer.convertClientId(context, _clientId);
939         }
940 
941         // -=Leonardo Uribe=- In jsf 1.1 and 1.2 this warning has sense, but in jsf 2.0 it is common to have
942         // components without any explicit id (UIViewParameter components and UIOuput resource components) instances.
943         // So, this warning is becoming obsolete in this new context and should be removed.
944         //if (idWasNull && log.isLoggable(Level.WARNING))
945         //{
946         //    log.warning("WARNING: Component " + _clientId
947         //            + " just got an automatic id, because there was no id assigned yet. "
948         //            + "If this component was created dynamically (i.e. not by a JSP tag) you should assign it an "
949         //            + "explicit static id or assign it the id you get from "
950         //            + "the createUniqueId from the current UIViewRoot "
951         //            + "component right after creation! Path to Component: " + getPathToComponent(this));
952         //}
953 
954         return _clientId;
955     }
956     
957     /**
958      * 
959      * @return
960      * 
961      * @since 2.0
962      */
963     public String getDefaultEventName()
964     {
965         // if a default event exists for a component, this method is overriden thus assume null
966         return null;
967     }
968     
969     /**
970      * 
971      * @return
972      * 
973      * @since 2.0
974      */
975     public Collection<String> getEventNames()
976     {
977         // must be specified by the implementing component.
978         // Returning null will force an error message in addClientBehavior.
979         return null;
980     }
981 
982     @Override
983     public UIComponent getFacet(String name)
984     {
985         return _facetMap == null ? null : _facetMap.get(name);
986     }
987 
988     /**
989      * @since 1.2
990      */
991     @Override
992     public int getFacetCount()
993     {
994         return _facetMap == null ? 0 : _facetMap.size();
995     }
996 
997     @Override
998     public Map<String, UIComponent> getFacets()
999     {
1000         if (_facetMap == null)
1001         {
1002             _facetMap = new _ComponentFacetMap<UIComponent>(this);
1003         }
1004         return _facetMap;
1005     }
1006 
1007     @Override
1008     public Iterator<UIComponent> getFacetsAndChildren()
1009     {
1010         // we can't use _facetMap and _childrenList here directly,
1011         // because some component implementation could keep their 
1012         // own properties for facets and children and just override
1013         // getFacets() and getChildren() (e.g. seen in PrimeFaces).
1014         // See MYFACES-2611 for details.
1015         if (getFacetCount() == 0)
1016         {
1017             if (getChildCount() == 0)
1018             {
1019                 return _EMPTY_UICOMPONENT_ITERATOR;
1020             }
1021 
1022             return getChildren().iterator();
1023         }
1024         else
1025         {
1026             if (getChildCount() == 0)
1027             {
1028                 return getFacets().values().iterator();
1029             }
1030 
1031             return new _FacetsAndChildrenIterator(getFacets(), getChildren());
1032         }
1033     }
1034 
1035     /**
1036      * Get a string which uniquely identifies this UIComponent within the scope of the nearest ancestor NamingContainer
1037      * component. The id is not necessarily unique across all components in the current view.
1038      */
1039     @JSFProperty(rtexprvalue = true)
1040     public String getId()
1041     {
1042         return _id;
1043     }
1044 
1045     @Override
1046     public UIComponent getParent()
1047     {
1048         return _parent;
1049     }
1050 
1051     @Override
1052     public String getRendererType()
1053     {
1054         // rendererType is literal-only, no ValueExpression - MYFACES-3136:
1055         // Even if this is true, according to JSF spec section 8 Rendering Model,
1056         // this part is essential to implement "delegated implementation" pattern,
1057         // so we can't do this optimization here. Instead, JSF developers could prevent
1058         // this evaluation overriding this method directly.
1059         if (_rendererType != null)
1060         {
1061             return _rendererType;
1062         }
1063         ValueExpression expression = getValueExpression("rendererType");
1064         if (expression != null)
1065         {
1066             return (String) expression.getValue(getFacesContext().getELContext());
1067         }
1068         return null;
1069     }
1070 
1071     /**
1072      * Indicates whether this component or its renderer manages the invocation of the rendering methods of its child
1073      * components. When this is true:
1074      * <ul>
1075      * <li>This component's encodeBegin method will only be called after all the child components have been created and
1076      * added to this component. <li>This component's encodeChildren method will be called after its encodeBegin method.
1077      * Components for which this method returns false do not get this method invoked at all. <li>No rendering methods
1078      * will be called automatically on child components; this component is required to invoke the
1079      * encodeBegin/encodeEnd/etc on them itself.
1080      * </ul>
1081      */
1082     @Override
1083     public boolean getRendersChildren()
1084     {
1085         Renderer renderer = getRenderer(getFacesContext());
1086         return renderer != null ? renderer.getRendersChildren() : false;
1087     }
1088 
1089     /**
1090      * Get the named value-binding associated with this component.
1091      * <p>
1092      * Value-bindings are stored in a map associated with the component, though there is commonly a property
1093      * (setter/getter methods) of the same name defined on the component itself which evaluates the value-binding when
1094      * called.
1095      * 
1096      * @deprecated Replaced by getValueExpression
1097      */
1098     @Override
1099     public ValueBinding getValueBinding(String name)
1100     {
1101         ValueExpression expression = getValueExpression(name);
1102         if (expression != null)
1103         {
1104             if (expression instanceof _ValueBindingToValueExpression)
1105             {
1106                 return ((_ValueBindingToValueExpression) expression).getValueBinding();
1107             }
1108             return new _ValueExpressionToValueBinding(expression);
1109         }
1110         return null;
1111     }
1112     
1113     public boolean initialStateMarked()
1114     {
1115         // TODO: IMPLEMENT HERE
1116         // FIXME: Nofity EG, this method should be in the specification
1117         return super.initialStateMarked();
1118     }
1119     
1120     /**
1121      * <code>invokeOnComponent</code> must be implemented in <code>UIComponentBase</code> too...
1122      */
1123     @Override
1124     public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
1125             throws FacesException
1126     {
1127         if (isCachedFacesContext())
1128         {
1129             return super.invokeOnComponent(context, clientId, callback);
1130         }
1131         else
1132         {
1133             try
1134             {
1135                 setCachedFacesContext(context);
1136                 return super.invokeOnComponent(context, clientId, callback);
1137             }
1138             finally
1139             {
1140                 setCachedFacesContext(null);
1141             }
1142         }
1143     }
1144     
1145     
1146 
1147     @Override
1148     public boolean visitTree(VisitContext context, VisitCallback callback)
1149     {
1150         if (isCachedFacesContext())
1151         {
1152             return super.visitTree(context, callback);
1153         }
1154         else
1155         {
1156             try
1157             {
1158                 setCachedFacesContext(context.getFacesContext());
1159                 return super.visitTree(context, callback);
1160             }
1161             finally
1162             {
1163                 setCachedFacesContext(null);
1164             }
1165         }
1166     }
1167 
1168     /**
1169      * A boolean value that indicates whether this component should be rendered. Default value: true.
1170      **/
1171     @Override
1172     @JSFProperty
1173     public boolean isRendered()
1174     {
1175         if (_cachedIsRendered != null)
1176         {
1177             return Boolean.TRUE.equals(_cachedIsRendered);
1178         }
1179         return (Boolean) getStateHelper().eval(PropertyKeys.rendered, DEFAULT_RENDERED);
1180     }
1181 
1182     @JSFProperty(literalOnly = true, istransient = true, tagExcluded = true)
1183     public boolean isTransient()
1184     {
1185         return _transient;
1186     }
1187     
1188     public void markInitialState()
1189     {
1190         super.markInitialState();
1191         if (_facesListeners != null)
1192         {
1193             _facesListeners.markInitialState();
1194         }
1195         if (_behaviorsMap != null)
1196         {
1197             for (Map.Entry<String, List<ClientBehavior> > entry : _behaviorsMap.entrySet())
1198             {
1199                 ((PartialStateHolder) entry.getValue()).markInitialState();
1200             }
1201         }
1202         if (_systemEventListenerClassMap != null)
1203         {
1204             for (Map.Entry<Class<? extends SystemEvent>, List<SystemEventListener>> entry : 
1205                 _systemEventListenerClassMap.entrySet())
1206             {
1207                 ((PartialStateHolder) entry.getValue()).markInitialState();
1208             }
1209         }
1210     }
1211 
1212     @Override
1213     protected void addFacesListener(FacesListener listener)
1214     {
1215         if (listener == null)
1216         {
1217             throw new NullPointerException("listener");
1218         }
1219         if (_facesListeners == null)
1220         {
1221             // How many facesListeners have single component normally? 
1222             _facesListeners = new _DeltaList<FacesListener>(new ArrayList<FacesListener>(5));
1223         }
1224         _facesListeners.add(listener);
1225     }
1226 
1227     @Override
1228     protected FacesContext getFacesContext()
1229     {
1230         if (_facesContext == null)
1231         {
1232             return FacesContext.getCurrentInstance();
1233         }
1234         else
1235         {
1236             return _facesContext;
1237         }
1238     }
1239 
1240     // FIXME: Notify EG for generic usage
1241     @Override
1242     protected FacesListener[] getFacesListeners(Class clazz)
1243     {
1244         if (clazz == null)
1245         {
1246             throw new NullPointerException("Class is null");
1247         }
1248         if (!FacesListener.class.isAssignableFrom(clazz))
1249         {
1250             throw new IllegalArgumentException("Class " + clazz.getName() + " must implement " + FacesListener.class);
1251         }
1252 
1253         if (_facesListeners == null)
1254         {
1255             return (FacesListener[]) Array.newInstance(clazz, 0);
1256         }
1257         List<FacesListener> lst = null;
1258         // perf: _facesListeners is RandomAccess instance (javax.faces.component._DeltaList)
1259         for (int i = 0, size = _facesListeners.size(); i < size; i++)
1260         {
1261             FacesListener facesListener = _facesListeners.get(i);
1262             if (facesListener != null && clazz.isAssignableFrom(facesListener.getClass()))
1263             {
1264                 if (lst == null)
1265                 {
1266                     lst = new ArrayList<FacesListener>();
1267                 }
1268                 lst.add(facesListener);
1269             }
1270         }
1271         if (lst == null)
1272         {
1273             return (FacesListener[]) Array.newInstance(clazz, 0);
1274         }
1275 
1276         return lst.toArray((FacesListener[]) Array.newInstance(clazz, lst.size()));
1277     }
1278 
1279     @Override
1280     protected Renderer getRenderer(FacesContext context)
1281     {
1282         if (context == null)
1283         {
1284             throw new NullPointerException("context");
1285         }
1286         Renderer renderer = getCachedRenderer();
1287         if (renderer != null)
1288         {
1289             return renderer;
1290         }
1291         String rendererType = getRendererType();
1292         if (rendererType == null)
1293         {
1294             return null;
1295         }
1296         
1297         RenderKit renderKit = context.getRenderKit();
1298         renderer = renderKit.getRenderer(getFamily(), rendererType);
1299         if (renderer == null)
1300         {
1301             String location = getComponentLocation(this);
1302             String logStr = "No Renderer found for component " + getPathToComponent(this)
1303                     + " (component-family=" + getFamily()
1304                     + ", renderer-type=" + rendererType + ")"
1305                     + (location != null ? " created from: " + location : "");
1306             
1307             getFacesContext().getExternalContext().log(logStr);
1308             log.warning(logStr);
1309         }
1310         return renderer;
1311     }
1312 
1313     @Override
1314     protected void removeFacesListener(FacesListener listener)
1315     {
1316         if (listener == null)
1317         {
1318             throw new NullPointerException("listener is null");
1319         }
1320 
1321         if (_facesListeners != null)
1322         {
1323             _facesListeners.remove(listener);
1324         }
1325     }
1326 
1327     @Override
1328     public void queueEvent(FacesEvent event)
1329     {
1330         if (event == null)
1331         {
1332             throw new NullPointerException("event");
1333         }
1334         UIComponent parent = getParent();
1335         if (parent == null)
1336         {
1337             throw new IllegalStateException("component is not a descendant of a UIViewRoot");
1338         }
1339         parent.queueEvent(event);
1340     }
1341 
1342     @Override
1343     public void processDecodes(FacesContext context)
1344     {
1345         try
1346         {
1347             setCachedFacesContext(context);
1348             // Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
1349             pushComponentToEL(context, this);
1350             if (_isPhaseExecutable(context))
1351             {
1352                 // Call the processDecodes() method of all facets and children of this UIComponent, in the order
1353                 // determined by a call to getFacetsAndChildren().
1354                 int facetCount = getFacetCount();
1355                 if (facetCount > 0)
1356                 {
1357                     for (UIComponent facet : getFacets().values())
1358                     {
1359                         facet.processDecodes(context);
1360                     }
1361                 }
1362                 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1363                 {
1364                     UIComponent child = getChildren().get(i);
1365                     child.processDecodes(context);
1366                 }
1367 
1368                 try
1369                 {
1370                     // Call the decode() method of this component.
1371                     decode(context);
1372                 }
1373                 catch (RuntimeException e)
1374                 {
1375                     // If a RuntimeException is thrown during decode processing, call FacesContext.renderResponse()
1376                     // and re-throw the exception.
1377                     context.renderResponse();
1378                     throw e;
1379                 }
1380             }
1381         }
1382         finally
1383         {
1384             // Call UIComponent.popComponentFromEL(javax.faces.context.FacesContext) from inside of a finally
1385             // block, just before returning.
1386 
1387             popComponentFromEL(context);
1388             setCachedFacesContext(null);
1389         }
1390     }
1391 
1392     @Override
1393     public void processValidators(FacesContext context)
1394     {
1395         try
1396         {
1397             setCachedFacesContext(context);
1398             // Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
1399             pushComponentToEL(context, this);
1400             if (_isPhaseExecutable(context))
1401             {
1402                 //Pre validation event dispatch for component
1403                 context.getApplication().publishEvent(context,  PreValidateEvent.class, getClass(), this);
1404                 
1405                 try
1406                 {
1407                     // Call the processValidators() method of all facets and children of this UIComponent, in the order
1408                     // determined by a call to getFacetsAndChildren().
1409                     int facetCount = getFacetCount();
1410                     if (facetCount > 0)
1411                     {
1412                         for (UIComponent facet : getFacets().values())
1413                         {
1414                             facet.processValidators(context);
1415                         }
1416                     }
1417     
1418                     for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1419                     {
1420                         UIComponent child = getChildren().get(i);
1421                         child.processValidators(context);
1422                     }
1423                 }
1424                 finally
1425                 {
1426                     context.getApplication().publishEvent(context,  PostValidateEvent.class, getClass(), this);
1427                 }
1428             }
1429         }
1430         finally
1431         {
1432             popComponentFromEL(context);
1433             setCachedFacesContext(null);
1434         }
1435     }
1436 
1437     /**
1438      * This isn't an input component, so just pass on the processUpdates call to child components and facets that might
1439      * be input components.
1440      * <p>
1441      * Components that were never rendered can't possibly be receiving update data (no corresponding fields were ever
1442      * put into the response) so if this component is not rendered then this method does not invoke processUpdates on
1443      * its children.
1444      */
1445     @Override
1446     public void processUpdates(FacesContext context)
1447     {
1448         try
1449         {
1450             setCachedFacesContext(context);
1451             // Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
1452             pushComponentToEL(context, this);
1453             if (_isPhaseExecutable(context))
1454             {
1455                 // Call the processUpdates() method of all facets and children of this UIComponent, in the order
1456                 // determined by a call to getFacetsAndChildren().
1457                 int facetCount = getFacetCount();
1458                 if (facetCount > 0)
1459                 {
1460                     for (UIComponent facet : getFacets().values())
1461                     {
1462                         facet.processUpdates(context);
1463                     }
1464                 }
1465 
1466                 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1467                 {
1468                     UIComponent child = getChildren().get(i);
1469                     child.processUpdates(context);
1470                 }
1471                 popComponentFromEL(context);
1472             }
1473         }
1474         finally
1475         {
1476             // After returning from the processUpdates() method on a child or facet, call
1477             // UIComponent.popComponentFromEL(javax.faces.context.FacesContext)
1478             setCachedFacesContext(null);
1479         }
1480     }
1481 
1482     @Override
1483     public Object processSaveState(FacesContext context)
1484     {
1485         if (context == null)
1486         {
1487             throw new NullPointerException("context");
1488         }
1489 
1490         if (isTransient())
1491         {
1492             // consult the transient property of this component. If true, just return null.
1493             return null;
1494         }
1495 
1496         // Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
1497         pushComponentToEL(context, this);
1498 
1499         Map<String, Object> facetMap;
1500 
1501         List<Object> childrenList;
1502         
1503         Object savedState;
1504         try
1505         {
1506             facetMap = null;
1507             int facetCount = getFacetCount();
1508             if (facetCount > 0)
1509             {
1510                 // Call the processSaveState() method of all facets and children of this UIComponent in the order
1511                 // determined by a call to getFacetsAndChildren(), skipping children and facets that are transient.
1512 
1513                 // To improve speed and robustness, the facets and children processing is splited to maintain the
1514                 // facet --> state coherence based on the facet's name
1515                 for (Map.Entry<String, UIComponent> entry : getFacets().entrySet())
1516                 {
1517                     UIComponent component = entry.getValue();
1518                     if (!component.isTransient())
1519                     {
1520                         if (facetMap == null)
1521                         {
1522                             facetMap = new HashMap<String, Object>(facetCount, 1);
1523                         }
1524 
1525                         facetMap.put(entry.getKey(), component.processSaveState(context));
1526 
1527                         // Ensure that UIComponent.popComponentFromEL(javax.faces.context.FacesContext) is called
1528                         // correctly after each child or facet.
1529                         // popComponentFromEL(context);
1530                     }
1531                 }
1532             }
1533             childrenList = null;
1534             int childCount = getChildCount();
1535             if (childCount > 0)
1536             {
1537                 // Call the processSaveState() method of all facets and children of this UIComponent in the order
1538                 // determined by a call to getFacetsAndChildren(), skipping children and facets that are transient.
1539 
1540                 // To improve speed and robustness, the facets and children processing is splited to maintain the
1541                 // facet --> state coherence based on the facet's name
1542                 for (int i = 0; i < childCount; i++)
1543                 {
1544                     UIComponent child = getChildren().get(i);
1545                     if (!child.isTransient())
1546                     {
1547                         if (childrenList == null)
1548                         {
1549                             childrenList = new ArrayList<Object>(childCount);
1550                         }
1551 
1552                         Object childState = child.processSaveState(context);
1553                         if (childState != null)
1554                         { // FIXME: Isn't that check dangerous for restoration since the child isn't marked transient?
1555                             childrenList.add(childState);
1556                         }
1557 
1558                         // Ensure that UIComponent.popComponentFromEL(javax.faces.context.FacesContext) is called
1559                         // correctly after each child or facet.
1560                     }
1561                 }
1562             }
1563             
1564             // Call the saveState() method of this component.
1565             savedState = saveState(context);
1566         }
1567         finally
1568         {
1569             popComponentFromEL(context);
1570         }
1571 
1572         // Encapsulate the child state and your state into a Serializable Object and return it.
1573         return new Object[] { savedState, facetMap, childrenList };
1574     }
1575 
1576     @SuppressWarnings("unchecked")
1577     @Override
1578     public void processRestoreState(FacesContext context, Object state)
1579     {
1580         if (context == null)
1581         {
1582             throw new NullPointerException("context");
1583         }
1584 
1585         Object[] stateValues = (Object[]) state;
1586 
1587         try
1588         {
1589             // Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
1590             pushComponentToEL(context, this);
1591 
1592             // Call the restoreState() method of this component.
1593             restoreState(context, stateValues[0]);
1594             
1595             Map<String, Object> facetMap = (Map<String, Object>) stateValues[1];
1596             if (facetMap != null && getFacetCount() > 0)
1597             {
1598                 // Call the processRestoreState() method of all facets and children of this UIComponent in the order
1599                 // determined by a call to getFacetsAndChildren().
1600 
1601                 // To improve speed and robustness, the facets and children processing is splited to maintain the
1602                 // facet --> state coherence based on the facet's name
1603                 for (Map.Entry<String, UIComponent> entry : getFacets().entrySet())
1604                 {
1605                     Object facetState = facetMap.get(entry.getKey());
1606                     if (facetState != null)
1607                     {
1608                         entry.getValue().processRestoreState(context, facetState);
1609 
1610                         // After returning from the processRestoreState() method on a child or facet, call
1611                         // UIComponent.popComponentFromEL(javax.faces.context.FacesContext)
1612                         // popComponentFromEL(context);
1613                     }
1614                     else
1615                     {
1616                         context.getExternalContext().log("No state found to restore facet " + entry.getKey());
1617                     }
1618                 }
1619             }
1620             List<Object> childrenList = (List<Object>) stateValues[2];
1621             if (childrenList != null && getChildCount() > 0)
1622             {
1623                 // Call the processRestoreState() method of all facets and children of this UIComponent in the order
1624                 // determined by a call to getFacetsAndChildren().
1625 
1626                 // To improve speed and robustness, the facets and children processing is splited to maintain the
1627                 // facet --> state coherence based on the facet's name
1628                 int idx = 0;
1629                 for (int i = 0, childCount = getChildCount(); i < childCount; i++)
1630                 {
1631                     UIComponent child = getChildren().get(i);
1632                     if (!child.isTransient())
1633                     {
1634                         Object childState = childrenList.get(idx++);
1635                         if (childState != null)
1636                         {
1637                             child.processRestoreState(context, childState);
1638 
1639                             // After returning from the processRestoreState() method on a child or facet, call
1640                             // UIComponent.popComponentFromEL(javax.faces.context.FacesContext)
1641                             // popComponentFromEL(context);
1642                         }
1643                         else
1644                         {
1645                             context.getExternalContext().log("No state found to restore child of component " + getId());
1646                         }
1647                     }
1648                 }
1649             }
1650         }
1651         finally
1652         {
1653             popComponentFromEL(context);
1654         }
1655     }
1656     
1657     /**
1658      * Gets the Location of the given UIComponent from its attribute map.
1659      * @param component
1660      * @return
1661      */
1662     private String getComponentLocation(UIComponent component)
1663     {
1664         Location location = (Location) component.getAttributes()
1665                 .get(UIComponent.VIEW_LOCATION_KEY);
1666         if (location != null)
1667         {
1668             return location.toString();
1669         }
1670         return null;
1671     }
1672 
1673     private String getPathToComponent(UIComponent component)
1674     {
1675         StringBuffer buf = new StringBuffer();
1676 
1677         if (component == null)
1678         {
1679             buf.append("{Component-Path : ");
1680             buf.append("[null]}");
1681             return buf.toString();
1682         }
1683 
1684         getPathToComponent(component, buf);
1685 
1686         buf.insert(0, "{Component-Path : ");
1687         buf.append("}");
1688 
1689         return buf.toString();
1690     }
1691 
1692     private void getPathToComponent(UIComponent component, StringBuffer buf)
1693     {
1694         if (component == null)
1695         {
1696             return;
1697         }
1698 
1699         StringBuffer intBuf = new StringBuffer();
1700 
1701         intBuf.append("[Class: ");
1702         intBuf.append(component.getClass().getName());
1703         if (component instanceof UIViewRoot)
1704         {
1705             intBuf.append(",ViewId: ");
1706             intBuf.append(((UIViewRoot) component).getViewId());
1707         }
1708         else
1709         {
1710             intBuf.append(",Id: ");
1711             intBuf.append(component.getId());
1712         }
1713         intBuf.append("]");
1714 
1715         buf.insert(0, intBuf.toString());
1716 
1717         getPathToComponent(component.getParent(), buf);
1718     }
1719 
1720     public void setTransient(boolean transientFlag)
1721     {
1722         _transient = transientFlag;
1723     }
1724 
1725     /**
1726      * Serializes objects which are "attached" to this component but which are not UIComponent children of it. Examples
1727      * are validator and listener objects. To be precise, it returns an object which implements java.io.Serializable,
1728      * and which when serialized will persist the state of the provided object.
1729      * <p>
1730      * If the attachedObject is a List then every object in the list is saved via a call to this method, and the
1731      * returned wrapper object contains a List object.
1732      * <p>
1733      * If the object implements StateHolder then the object's saveState is called immediately, and a wrapper is returned
1734      * which contains both this saved state and the original class name. However in the case where the
1735      * StateHolder.isTransient method returns true, null is returned instead.
1736      * <p>
1737      * If the object implements java.io.Serializable then the object is simply returned immediately; standard java
1738      * serialization will later be used to store this object.
1739      * <p>
1740      * In all other cases, a wrapper is returned which simply stores the type of the provided object. When deserialized,
1741      * a default instance of that type will be recreated.
1742      */
1743     public static Object saveAttachedState(FacesContext context, Object attachedObject)
1744     {
1745         if (context == null)
1746         {
1747             throw new NullPointerException ("context");
1748         }
1749         
1750         if (attachedObject == null)
1751         {
1752             return null;
1753         }
1754         // StateHolder interface should take precedence over
1755         // List children
1756         if (attachedObject instanceof StateHolder)
1757         {
1758             StateHolder holder = (StateHolder) attachedObject;
1759             if (holder.isTransient())
1760             {
1761                 return null;
1762             }
1763 
1764             return new _AttachedStateWrapper(attachedObject.getClass(), holder.saveState(context));
1765         }
1766         else if (attachedObject instanceof Collection)
1767         {
1768             if (ArrayList.class.equals(attachedObject.getClass()))
1769             {
1770                 ArrayList<?> list = (ArrayList<?>) attachedObject;
1771                 int size = list.size();
1772                 List<Object> lst = new ArrayList<Object>(size);
1773                 for (int i = 0; i < size; i++)
1774                 {
1775                     Object item = list.get(i);
1776                     if (item != null)
1777                     {
1778                         lst.add(saveAttachedState(context, item));
1779                     }
1780                 }
1781                 return new _AttachedListStateWrapper(lst);
1782             }
1783             else
1784             {
1785                 List<Object> lst = new ArrayList<Object>(((Collection<?>) attachedObject).size());
1786                 for (Object item : (Collection<?>) attachedObject)
1787                 {
1788                     if (item != null)
1789                     {
1790                         lst.add(saveAttachedState(context, item));
1791                     }
1792                 }
1793                 return new _AttachedCollectionStateWrapper(attachedObject.getClass(), lst);
1794             }
1795         }
1796         else if (attachedObject instanceof Serializable)
1797         {
1798             return attachedObject;
1799         }
1800         else
1801         {
1802             return new _AttachedStateWrapper(attachedObject.getClass(), null);
1803         }
1804     }
1805 
1806     public static Object restoreAttachedState(FacesContext context, Object stateObj) throws IllegalStateException
1807     {
1808         if (context == null)
1809         {
1810             throw new NullPointerException("context");
1811         }
1812         if (stateObj == null)
1813         {
1814             return null;
1815         }
1816         if (stateObj instanceof _AttachedListStateWrapper)
1817         {
1818             // perf: getWrappedStateList in _AttachedListStateWrapper is always ArrayList: see saveAttachedState
1819             ArrayList<Object> lst = (ArrayList<Object>) ((_AttachedListStateWrapper) stateObj).getWrappedStateList();
1820             List<Object> restoredList = new ArrayList<Object>(lst.size());
1821             for (int i = 0, size = lst.size(); i < size; i++)
1822             {
1823                 Object item = lst.get(i);
1824                 restoredList.add(restoreAttachedState(context, item));
1825             }
1826             return restoredList;
1827         }
1828         else if (stateObj instanceof _AttachedCollectionStateWrapper)
1829         {
1830             _AttachedCollectionStateWrapper wrappedState = (_AttachedCollectionStateWrapper) stateObj; 
1831             Class<?> clazz = wrappedState.getClazz();
1832             List<Object> lst = wrappedState.getWrappedStateList();
1833             Collection restoredList;
1834             try
1835             {
1836                 restoredList = (Collection) clazz.newInstance();
1837             }
1838             catch (InstantiationException e)
1839             {
1840                 throw new RuntimeException("Could not restore StateHolder of type " + clazz.getName()
1841                         + " (missing no-args constructor?)", e);
1842             }
1843             catch (IllegalAccessException e)
1844             {
1845                 throw new RuntimeException(e);
1846             }
1847 
1848             for (Object item : lst)
1849             {
1850                 restoredList.add(restoreAttachedState(context, item));
1851             }
1852             return restoredList;
1853 
1854         }
1855         else if (stateObj instanceof _AttachedStateWrapper)
1856         {
1857             Class<?> clazz = ((_AttachedStateWrapper) stateObj).getClazz();
1858             Object restoredObject;
1859             try
1860             {
1861                 restoredObject = clazz.newInstance();
1862             }
1863             catch (InstantiationException e)
1864             {
1865                 throw new RuntimeException("Could not restore StateHolder of type " + clazz.getName()
1866                         + " (missing no-args constructor?)", e);
1867             }
1868             catch (IllegalAccessException e)
1869             {
1870                 throw new RuntimeException(e);
1871             }
1872             if (restoredObject instanceof StateHolder)
1873             {
1874                 _AttachedStateWrapper wrapper = (_AttachedStateWrapper) stateObj;
1875                 Object wrappedState = wrapper.getWrappedStateObject();
1876 
1877                 StateHolder holder = (StateHolder) restoredObject;
1878                 holder.restoreState(context, wrappedState);
1879             }
1880             return restoredObject;
1881         }
1882         else
1883         {
1884             return stateObj;
1885         }
1886     }
1887 
1888     /**
1889      * Invoked after the render phase has completed, this method returns an object which can be passed to the
1890      * restoreState of some other instance of UIComponentBase to reset that object's state to the same values as this
1891      * object currently has.
1892      */
1893     public Object saveState(FacesContext context)
1894     {
1895         if (context == null)
1896         {
1897             throw new NullPointerException ("context");
1898         }
1899         
1900         if (initialStateMarked())
1901         {
1902             //Delta
1903             //_id and _clientId was already restored from template
1904             //and never changes during component life.
1905             Object facesListenersSaved = saveFacesListenersList(context);
1906             Object behaviorsMapSaved = saveBehaviorsMap(context);
1907             Object systemEventListenerClassMapSaved = saveSystemEventListenerClassMap(context);
1908             Object stateHelperSaved = null;
1909             StateHelper stateHelper = getStateHelper(false);
1910             if (stateHelper != null)
1911             {
1912                 stateHelperSaved = stateHelper.saveState(context);
1913             }
1914             
1915             if (facesListenersSaved == null && stateHelperSaved == null && 
1916                 behaviorsMapSaved == null && systemEventListenerClassMapSaved == null &&
1917                !_isRendererTypeSet)
1918             {
1919                 return null;
1920             }
1921             
1922             if (_isRendererTypeSet)
1923             {
1924                 return new Object[] {facesListenersSaved, stateHelperSaved, behaviorsMapSaved,
1925                                     systemEventListenerClassMapSaved, 
1926                                     _rendererType};
1927             }
1928             else
1929             {
1930                 return new Object[] {facesListenersSaved, stateHelperSaved, behaviorsMapSaved,
1931                     systemEventListenerClassMapSaved};
1932             }
1933         }
1934         else
1935         {
1936             //Full
1937             Object values[] = new Object[9];
1938             values[0] = saveFacesListenersList(context);
1939             StateHelper stateHelper = getStateHelper(false);
1940             if (stateHelper != null)
1941             {
1942                 values[1] = stateHelper.saveState(context);
1943             }
1944             values[2] = saveBehaviorsMap(context);
1945             values[3] = saveSystemEventListenerClassMap(context);
1946             values[4] = _id;
1947             values[5] = _clientId;
1948             values[6] = _markCreated;
1949             values[7] = _rendererType;
1950             values[8] = _isRendererTypeSet;
1951 
1952             return values;
1953         }
1954     }
1955 
1956     /**
1957      * Invoked in the "restore view" phase, this initialises this object's members from the values saved previously into
1958      * the provided state object.
1959      * <p>
1960      * 
1961      * @param state
1962      *            is an object previously returned by the saveState method of this class.
1963      */
1964     @SuppressWarnings("unchecked")
1965     public void restoreState(FacesContext context, Object state)
1966     {
1967         if (context == null)
1968         {
1969             throw new NullPointerException ("context");
1970         }
1971         
1972         if (state == null)
1973         {
1974             //Only happens if initialStateMarked return true
1975             
1976             if (initialStateMarked())
1977             {
1978                 return;
1979             }
1980             
1981             throw new NullPointerException ("state");
1982         }
1983         
1984         Object values[] = (Object[]) state;
1985         
1986         if ( values.length == 9 && initialStateMarked())
1987         {
1988             //Delta mode is active, but we are restoring a full state.
1989             //we need to clear the initial state, to restore state without
1990             //take into account delta.
1991             clearInitialState();
1992         }
1993         
1994         if (values[0] instanceof _AttachedDeltaWrapper)
1995         {
1996             //Delta: check for null is not necessary since _facesListener field
1997             //is only set once and never reset
1998             //if (_facesListeners != null)
1999             //{
2000                 ((StateHolder)_facesListeners).restoreState(context,
2001                         ((_AttachedDeltaWrapper) values[0]).getWrappedStateObject());
2002             //}
2003         }
2004         else if (values[0] != null || (values.length == 9))
2005         {
2006             //Full
2007             _facesListeners = (_DeltaList<FacesListener>)
2008                 restoreAttachedState(context,values[0]);
2009         }
2010         // Note that if values[0] == null && initialStateMarked(),
2011         // means delta is null, not that _facesListeners == null. 
2012         // We can do this here because _facesListeners instance once
2013         // is created is never replaced or set to null.
2014         
2015         getStateHelper().restoreState(context, values[1]);
2016         
2017         if (values.length == 9)
2018         {
2019             _id = (String) values[4];
2020             _clientId = (String) values[5];
2021             _markCreated = (String) values[6];
2022             _rendererType = (String) values[7];
2023             _isRendererTypeSet = (Boolean) values[8];
2024         }
2025         else if (values.length == 5)
2026         {
2027             _rendererType = (String) values[4];
2028             _isRendererTypeSet = true;
2029         }
2030 
2031         // rendererType needs to be restored before SystemEventListener,
2032         // otherwise UIComponent.getCurrentComponent(context).getRenderer(context)
2033         // will not work correctly
2034         if (values.length == 9)
2035         {
2036             //Full restore
2037             restoreFullBehaviorsMap(context, values[2]);
2038             restoreFullSystemEventListenerClassMap(context, values[3]);
2039         }
2040         else
2041         {
2042             //Delta restore
2043             restoreDeltaBehaviorsMap(context, values[2]);
2044             restoreDeltaSystemEventListenerClassMap(context, values[3]);
2045         }
2046     }
2047     
2048     private Object saveFacesListenersList(FacesContext facesContext)
2049     {
2050         PartialStateHolder holder = (PartialStateHolder) _facesListeners;
2051         if (initialStateMarked() && _facesListeners != null && holder.initialStateMarked())
2052         {                
2053             Object attachedState = holder.saveState(facesContext);
2054             if (attachedState != null)
2055             {
2056                 return new _AttachedDeltaWrapper(_facesListeners.getClass(),
2057                         attachedState);
2058             }
2059             //_facesListeners instances once is created never changes, we can return null
2060             return null;
2061         }
2062         else
2063         {
2064             return saveAttachedState(facesContext,_facesListeners);
2065         }            
2066     }
2067 
2068     @SuppressWarnings("unchecked")
2069     private void restoreFullBehaviorsMap(FacesContext facesContext, Object stateObj)
2070     {
2071         if (stateObj != null)
2072         {
2073             Map<String, Object> stateMap = (Map<String, Object>) stateObj;
2074             int initCapacity = (stateMap.size() * 4 + 3) / 3;
2075             _behaviorsMap = new HashMap<String,  List<ClientBehavior> >(initCapacity);
2076             _unmodifiableBehaviorsMap = null;
2077             for (Map.Entry<String, Object> entry : stateMap.entrySet())
2078             {
2079                 _behaviorsMap.put(entry.getKey(),
2080                                   (List<ClientBehavior>) restoreAttachedState(facesContext, entry.getValue()));
2081             }
2082         }
2083         else
2084         {
2085             _behaviorsMap = null;
2086             _unmodifiableBehaviorsMap = null;
2087         }        
2088     }
2089     
2090     @SuppressWarnings("unchecked")
2091     private void restoreDeltaBehaviorsMap(FacesContext facesContext, Object stateObj)
2092     {
2093         if (stateObj != null)
2094         {
2095             _unmodifiableBehaviorsMap = null;
2096             Map<String, Object> stateMap = (Map<String, Object>) stateObj;
2097             int initCapacity = (stateMap.size() * 4 + 3) / 3;
2098             if (_behaviorsMap == null)
2099             {
2100                 _behaviorsMap = new HashMap<String,  List<ClientBehavior> >(initCapacity);
2101             }
2102             for (Map.Entry<String, Object> entry : stateMap.entrySet())
2103             {
2104                 Object savedObject = entry.getValue(); 
2105                 if (savedObject instanceof _AttachedDeltaWrapper)
2106                 {
2107                     StateHolder holderList = (StateHolder) _behaviorsMap.get(entry.getKey());
2108                     holderList.restoreState(facesContext,
2109                                             ((_AttachedDeltaWrapper) savedObject).getWrappedStateObject());
2110                 }
2111                 else
2112                 {
2113                     _behaviorsMap.put(entry.getKey(),
2114                                       (List<ClientBehavior>) restoreAttachedState(facesContext, savedObject));
2115                 }
2116             }
2117         }
2118     }
2119     
2120     private Object saveBehaviorsMap(FacesContext facesContext)
2121     {
2122         if (_behaviorsMap != null)
2123         {
2124             if (initialStateMarked())
2125             {
2126                 HashMap<String, Object> stateMap = new HashMap<String, Object>(_behaviorsMap.size(), 1);
2127                 boolean nullDelta = true;
2128                 for (Map.Entry<String, List<ClientBehavior> > entry : _behaviorsMap.entrySet())
2129                 {
2130                     // The list is always an instance of _DeltaList so we can cast to
2131                     // PartialStateHolder 
2132                     PartialStateHolder holder = (PartialStateHolder) entry.getValue();
2133                     if (holder.initialStateMarked())
2134                     {
2135                         Object attachedState = holder.saveState(facesContext);
2136                         if (attachedState != null)
2137                         {
2138                             stateMap.put(entry.getKey(), new _AttachedDeltaWrapper(_behaviorsMap.getClass(),
2139                                     attachedState));
2140                             nullDelta = false;
2141                         }
2142                     }
2143                     else
2144                     {
2145                         stateMap.put(entry.getKey(), saveAttachedState(facesContext, holder));
2146                         nullDelta = false;
2147                     }
2148                 }
2149                 if (nullDelta)
2150                 {
2151                     return null;
2152                 }
2153                 return stateMap;
2154             }
2155             else
2156             {
2157                 //Save it in the traditional way
2158                 HashMap<String, Object> stateMap = 
2159                     new HashMap<String, Object>(_behaviorsMap.size(), 1);
2160                 for (Map.Entry<String, List<ClientBehavior> > entry : _behaviorsMap.entrySet())
2161                 {
2162                     stateMap.put(entry.getKey(), saveAttachedState(facesContext, entry.getValue()));
2163                 }
2164                 return stateMap;
2165             }
2166         }
2167         else
2168         {
2169             return null;
2170         }
2171     }
2172     
2173     @SuppressWarnings("unchecked")
2174     private void restoreFullSystemEventListenerClassMap(FacesContext facesContext, Object stateObj)
2175     {
2176         if (stateObj != null)
2177         {
2178             Map<Class<? extends SystemEvent>, Object> stateMap = (Map<Class<? extends SystemEvent>, Object>) stateObj;
2179             int initCapacity = (stateMap.size() * 4 + 3) / 3;
2180             _systemEventListenerClassMap
2181                     = new HashMap<Class<? extends SystemEvent>, List<SystemEventListener>>(initCapacity);
2182             for (Map.Entry<Class<? extends SystemEvent>, Object> entry : stateMap.entrySet())
2183             {
2184                 _systemEventListenerClassMap.put(entry.getKey(),
2185                         (List<SystemEventListener>) restoreAttachedState(facesContext, entry.getValue()));
2186             }
2187         }
2188         else
2189         {
2190             _systemEventListenerClassMap = null;
2191         }        
2192     }
2193     
2194     @SuppressWarnings("unchecked")
2195     private void restoreDeltaSystemEventListenerClassMap(FacesContext facesContext, Object stateObj)
2196     {
2197         if (stateObj != null)
2198         {
2199             Map<Class<? extends SystemEvent>, Object> stateMap = (Map<Class<? extends SystemEvent>, Object>) stateObj;
2200             int initCapacity = (stateMap.size() * 4 + 3) / 3;
2201             if (_systemEventListenerClassMap == null)
2202             {
2203                 _systemEventListenerClassMap
2204                         = new HashMap<Class<? extends SystemEvent>, List<SystemEventListener>>(initCapacity);
2205             }
2206             for (Map.Entry<Class<? extends SystemEvent>, Object> entry : stateMap.entrySet())
2207             {
2208                 Object savedObject = entry.getValue(); 
2209                 if (savedObject instanceof _AttachedDeltaWrapper)
2210                 {
2211                     StateHolder holderList = (StateHolder) _systemEventListenerClassMap.get(entry.getKey());
2212                     holderList.restoreState(facesContext,
2213                                             ((_AttachedDeltaWrapper) savedObject).getWrappedStateObject());
2214                 }
2215                 else
2216                 {
2217                     _systemEventListenerClassMap.put(entry.getKey(),
2218                             (List<SystemEventListener>) restoreAttachedState(facesContext, savedObject));
2219                 }
2220             }
2221         }
2222     }
2223     
2224     private Object saveSystemEventListenerClassMap(FacesContext facesContext)
2225     {
2226         if (_systemEventListenerClassMap != null)
2227         {
2228             if (initialStateMarked())
2229             {
2230                 HashMap<Class<? extends SystemEvent>, Object> stateMap
2231                         = new HashMap<Class<? extends SystemEvent>, Object>(_systemEventListenerClassMap.size(), 1);
2232                 boolean nullDelta = true;
2233                 for (Map.Entry<Class<? extends SystemEvent>, List<SystemEventListener> > entry
2234                         : _systemEventListenerClassMap.entrySet())
2235                 {
2236                     // The list is always an instance of _DeltaList so we can cast to
2237                     // PartialStateHolder 
2238                     PartialStateHolder holder = (PartialStateHolder) entry.getValue();
2239                     if (holder.initialStateMarked())
2240                     {
2241                         Object attachedState = holder.saveState(facesContext);
2242                         if (attachedState != null)
2243                         {
2244                             stateMap.put(entry.getKey(),
2245                                     new _AttachedDeltaWrapper(_systemEventListenerClassMap.getClass(), attachedState));
2246                             nullDelta = false;
2247                         }
2248                     }
2249                     else
2250                     {
2251                         stateMap.put(entry.getKey(), saveAttachedState(facesContext, holder));
2252                         nullDelta = false;
2253                     }
2254                 }
2255                 if (nullDelta)
2256                 {
2257                     return null;
2258                 }
2259                 return stateMap;
2260             }
2261             else
2262             {
2263                 //Save it in the traditional way
2264                 HashMap<Class<? extends SystemEvent>, Object> stateMap = 
2265                     new HashMap<Class<? extends SystemEvent>, Object>(_systemEventListenerClassMap.size(), 1);
2266                 for (Map.Entry<Class<? extends SystemEvent>, List<SystemEventListener> > entry
2267                         : _systemEventListenerClassMap.entrySet())
2268                 {
2269                     stateMap.put(entry.getKey(), saveAttachedState(facesContext, entry.getValue()));
2270                 }
2271                 return stateMap;
2272             }
2273         }
2274         else
2275         {
2276             return null;
2277         }
2278     }
2279     
2280     /*
2281     private Object saveBindings(FacesContext context)
2282     {
2283         if (bindings != null)
2284         {
2285             HashMap<String, Object> stateMap = new HashMap<String, Object>(bindings.size(), 1);
2286             for (Iterator<Entry<String, ValueExpression>> it = bindings.entrySet().iterator(); it.hasNext();)
2287             {
2288                 Entry<String, ValueExpression> entry = it.next();
2289                 stateMap.put(entry.getKey(), saveAttachedState(context, entry.getValue()));
2290             }
2291             return stateMap;
2292         }
2293 
2294         return null;
2295     }
2296 
2297     @SuppressWarnings("unchecked")
2298     private void restoreValueExpressionMap(FacesContext context, Object stateObj)
2299     {
2300         if (stateObj != null)
2301         {
2302             Map<String, Object> stateMap = (Map<String, Object>) stateObj;
2303             int initCapacity = (stateMap.size() * 4 + 3) / 3;
2304             bindings = new HashMap<String, ValueExpression>(initCapacity);
2305             for (Map.Entry<String, Object> entry : stateMap.entrySet())
2306             {
2307                 bindings.put(entry.getKey(), (ValueExpression) restoreAttachedState(context, entry.getValue()));
2308             }
2309         }
2310         else
2311         {
2312             bindings = null;
2313         }
2314     }*/
2315 
2316     /**
2317      * @param string
2318      *            the component id, that should be a vaild one.
2319      */
2320     private void isIdValid(String string)
2321     {
2322 
2323         // is there any component identifier ?
2324         if (string == null)
2325         {
2326             return;
2327         }
2328 
2329         // Component identifiers must obey the following syntax restrictions:
2330         // 1. Must not be a zero-length String.
2331         if (string.length() == 0)
2332         {
2333             throw new IllegalArgumentException("component identifier must not be a zero-length String");
2334         }
2335 
2336         // If new id is the same as old it must be valid
2337         if (string.equals(_id))
2338         {
2339             return;
2340         }
2341 
2342         // 2. First character must be a letter or an underscore ('_').
2343         if (!Character.isLetter(string.charAt(0)) && string.charAt(0) != '_')
2344         {
2345             throw new IllegalArgumentException("component identifier's first character must be a letter "
2346                                                + "or an underscore ('_')! But it is \""
2347                                                + string.charAt(0) + "\"");
2348         }
2349         for (int i = 1; i < string.length(); i++)
2350         {
2351             char c = string.charAt(i);
2352             // 3. Subsequent characters must be a letter, a digit, an underscore ('_'), or a dash ('-').
2353             if (!Character.isLetterOrDigit(c) && c != '-' && c != '_')
2354             {
2355                 throw new IllegalArgumentException("Subsequent characters of component identifier must be a letter, "
2356                                                    + "a digit, an underscore ('_'), or a dash ('-')! "
2357                                                    + "But component identifier contains \""
2358                                                    + c + "\"");
2359             }
2360         }
2361     }
2362 
2363     private boolean _isPhaseExecutable(FacesContext context)
2364     {
2365         if (context == null)
2366         {
2367             throw new NullPointerException("context");
2368         }
2369 
2370         // If the rendered property of this UIComponent is false, skip further processing.
2371         return isRendered();
2372     }
2373 
2374     boolean isCachedFacesContext()
2375     {
2376         return _facesContext != null;
2377     }
2378     
2379     void setCachedFacesContext(FacesContext facesContext)
2380     {
2381         _facesContext = facesContext;
2382     }
2383     
2384     Renderer getCachedRenderer()
2385     {
2386         return _cachedRenderer;
2387     }
2388     
2389     void setCachedRenderer(Renderer renderer)
2390     {
2391         _cachedRenderer = renderer;
2392     }
2393 
2394     Boolean isCachedIsRendered()
2395     {
2396         return _cachedIsRendered;
2397     }
2398     
2399     void setCachedIsRendered(Boolean rendered)
2400     {
2401        _cachedIsRendered = rendered;
2402     }
2403     
2404     <T> T getExpressionValue(String attribute, T explizitValue, T defaultValueIfExpressionNull)
2405     {
2406         return _ComponentUtils.getExpressionValue(this, attribute, explizitValue, defaultValueIfExpressionNull);
2407     }
2408 
2409     void setOamVfMarkCreated(String markCreated)
2410     {
2411         _markCreated = markCreated;
2412     }
2413     
2414     String getOamVfMarkCreated()
2415     {
2416         return _markCreated;
2417     }
2418     
2419     String getOamVfFacetName()
2420     {
2421         return _facetName;
2422     }
2423     
2424     void setOamVfFacetName(String facetName)
2425     {
2426         _facetName = facetName;
2427     }
2428     
2429 /**
2430      * <p>
2431      * This gets a single FacesContext-local shared stringbuilder instance, each time you call
2432      * __getSharedStringBuilder it sets the length of the stringBuilder instance to 0.
2433      * </p><p>
2434      * This allows you to use the same StringBuilder instance over and over.
2435      * You must call toString on the instance before calling __getSharedStringBuilder again.
2436      * </p>
2437      * Example that works
2438      * <pre><code>
2439      * StringBuilder sb1 = __getSharedStringBuilder();
2440      * sb1.append(a).append(b);
2441      * String c = sb1.toString();
2442      *
2443      * StringBuilder sb2 = __getSharedStringBuilder();
2444      * sb2.append(b).append(a);
2445      * String d = sb2.toString();
2446      * </code></pre>
2447      * <br><br>
2448      * Example that doesn't work, you must call toString on sb1 before
2449      * calling __getSharedStringBuilder again.
2450      * <pre><code>
2451      * StringBuilder sb1 = __getSharedStringBuilder();
2452      * StringBuilder sb2 = __getSharedStringBuilder();
2453      *
2454      * sb1.append(a).append(b);
2455      * String c = sb1.toString();
2456      *
2457      * sb2.append(b).append(a);
2458      * String d = sb2.toString();
2459      * </code></pre>
2460      *
2461      */
2462     static StringBuilder __getSharedStringBuilder()
2463     {
2464         return __getSharedStringBuilder(FacesContext.getCurrentInstance());
2465     }
2466 
2467     // TODO checkstyle complains; does this have to lead with __ ?
2468     static StringBuilder __getSharedStringBuilder(FacesContext facesContext)
2469     {
2470         Map<Object, Object> attributes = facesContext.getAttributes();
2471 
2472         StringBuilder sb = (StringBuilder) attributes.get(_STRING_BUILDER_KEY);
2473 
2474         if (sb == null)
2475         {
2476             sb = new StringBuilder();
2477             attributes.put(_STRING_BUILDER_KEY, sb);
2478         }
2479         else
2480         {
2481 
2482             // clear out the stringBuilder by setting the length to 0
2483             sb.setLength(0);
2484         }
2485 
2486         return sb;
2487     }
2488 
2489     // ------------------ GENERATED CODE BEGIN (do not modify!) --------------------
2490 
2491     private static final Boolean DEFAULT_RENDERED = Boolean.TRUE;
2492 
2493     @Override
2494     public void setRendered(boolean rendered)
2495     {
2496         getStateHelper().put(PropertyKeys.rendered, rendered );
2497         setCachedIsRendered(null);
2498     }
2499 
2500     @Override
2501     public void setRendererType(String rendererType)
2502     {
2503         this._rendererType = rendererType;
2504         if (initialStateMarked())
2505         {
2506             //This flag just indicates the rendererType 
2507             //should be included on the delta
2508             this._isRendererTypeSet = true;
2509         }
2510         setCachedRenderer(null);
2511     }
2512 
2513     // ------------------ GENERATED CODE END ---------------------------------------
2514 
2515     private Map<String, List<ClientBehavior>> wrapBehaviorsMap()
2516     {
2517         if (_unmodifiableBehaviorsMap == null)
2518         {
2519             _unmodifiableBehaviorsMap = Collections.unmodifiableMap(_behaviorsMap); 
2520         }
2521         return _unmodifiableBehaviorsMap; 
2522     }
2523 }