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 java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Enumeration;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.Map;
32  import java.util.MissingResourceException;
33  import java.util.PropertyResourceBundle;
34  import java.util.ResourceBundle;
35  import java.util.Set;
36  
37  import javax.el.ELException;
38  import javax.el.ValueExpression;
39  import javax.faces.FacesException;
40  import javax.faces.application.Resource;
41  import javax.faces.component.visit.VisitCallback;
42  import javax.faces.component.visit.VisitContext;
43  import javax.faces.component.visit.VisitHint;
44  import javax.faces.component.visit.VisitResult;
45  import javax.faces.context.FacesContext;
46  import javax.faces.el.ValueBinding;
47  import javax.faces.event.AbortProcessingException;
48  import javax.faces.event.ComponentSystemEvent;
49  import javax.faces.event.ComponentSystemEventListener;
50  import javax.faces.event.FacesEvent;
51  import javax.faces.event.FacesListener;
52  import javax.faces.event.PostRestoreStateEvent;
53  import javax.faces.event.SystemEvent;
54  import javax.faces.event.SystemEventListener;
55  import javax.faces.event.SystemEventListenerHolder;
56  import javax.faces.render.Renderer;
57  import javax.faces.render.RendererWrapper;
58  
59  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
60  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
61  
62  /**
63   *
64   * see Javadoc of <a href="http://java.sun.com/javaee/javaserverfaces/1.2/docs/api/index.html">J
65   * SF Specification</a>
66   */
67  @JSFComponent(type = "javax.faces.Component", family = "javax.faces.Component",
68                desc = "abstract base component", configExcluded = true)
69  public abstract class UIComponent
70          implements PartialStateHolder, TransientStateHolder, SystemEventListenerHolder, ComponentSystemEventListener
71  {
72      // TODO: Reorder methods, this class is a mess
73      /**
74       * Constant used in component attribute map to retrieve the BeanInfo of a composite
75       * component.
76       *
77       * @see javax.faces.view.ViewDeclarationLanguage#getComponentMetadata(FacesContext, Resource)
78       * @see javax.faces.view.ViewDeclarationLanguage#retargetAttachedObjects(FacesContext, UIComponent, List)
79       * @see javax.faces.view.ViewDeclarationLanguage#retargetMethodExpressions(FacesContext, UIComponent)
80       * @see javax.faces.application.Application#createComponent(FacesContext, Resource)
81       */
82      public static final String BEANINFO_KEY = "javax.faces.component.BEANINFO_KEY";
83  
84      /**
85       * Constant used in BeanInfo descriptor as a key for retrieve an alternate component type
86       * for create the composite base component. 
87       *
88       * @see javax.faces.application.Application#createComponent(FacesContext, Resource)
89       */
90      public static final String COMPOSITE_COMPONENT_TYPE_KEY = "javax.faces.component.COMPOSITE_COMPONENT_TYPE";
91  
92      /**
93       * Constant used to define the facet inside this component that store the component hierarchy
94       * generated by a composite component implementation, and then rendered. In other words, 
95       * note that direct children of a component are not rendered, instead components inside 
96       * this face are rendered.
97       */
98      public static final String COMPOSITE_FACET_NAME = "javax.faces.component.COMPOSITE_FACET_NAME";
99  
100     /**
101      * Constant used to store the current component that is being processed.
102      *
103      * @see #pushComponentToEL(FacesContext, UIComponent)
104      * @see #popComponentFromEL(FacesContext)
105      */
106     public static final String CURRENT_COMPONENT = "javax.faces.component.CURRENT_COMPONENT";
107 
108     /**
109      * Constant used to store the current composite component that is being processed. 
110      *
111      * @see #pushComponentToEL(FacesContext, UIComponent)
112      * @see #popComponentFromEL(FacesContext)
113      */
114     public static final String CURRENT_COMPOSITE_COMPONENT = "javax.faces.component.CURRENT_COMPOSITE_COMPONENT";
115 
116     /**
117      * This constant has two usages. The first one is in component attribute map to identify the 
118      * facet name under this component is child of its parent. The second one is on BeanInfo descriptor
119      * as a key for a Map&lt;String, PropertyDescriptor&gt; that contains metadata information defined
120      * by composite:facet tag and composite:implementation(because this one fills the facet referenced
121      * by COMPOSITE_FACET_NAME constant). 
122      */
123     public static final String FACETS_KEY = "javax.faces.component.FACETS_KEY";
124 
125     /**
126      * Constant used in component attribute map to store the {@link javax.faces.view.Location} object
127      * where the definition of this component is.
128      */
129     public static final String VIEW_LOCATION_KEY = "javax.faces.component.VIEW_LOCATION_KEY";
130 
131     public static final String ATTRS_WITH_DECLARED_DEFAULT_VALUES
132             = "javax.faces.component.ATTR_NAMES_WITH_DEFAULT_VALUES";
133 
134     /**
135      * Indicate if the facesContext attribute values under the keys javax.faces.component.CURRENT_COMPONENT and
136      * javax.faces.component.CURRENT_COMPOSITE_COMPONENT should be valid or not. By default, those keys are
137      * deprecated since 2.1
138      */
139     @JSFWebConfigParam(since = "2.1.0", expectedValues = "true, false", defaultValue = "false")
140     public static final String HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME
141             = "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES";
142 
143     /**
144      * The key under which the component stack is stored in the FacesContext.
145      * ATTENTION: this constant is duplicate in CompositeComponentExpressionUtils.
146      */
147     private static final String _COMPONENT_STACK = "componentStack:" + UIComponent.class.getName();
148 
149     private static final String _CURRENT_COMPOSITE_COMPONENT_KEY = "compositeComponent:" + UIComponent.class.getName();
150 
151     Map<Class<? extends SystemEvent>, List<SystemEventListener>> _systemEventListenerClassMap;
152 
153     /**
154      * @deprecated
155      */
156     @Deprecated
157     protected Map<String, ValueExpression> bindings;
158     /**
159      * Used to cache the map created using getResourceBundleMap() method, since this method could be called several
160      * times when rendering the composite component. This attribute may not be serialized, so transient is used (There
161      * are some very few special cases when UIComponent instances are serializable like t:schedule, so it is better if
162      * transient is used).
163      */
164     private transient Map<String, String> _resourceBundleMap = null;
165     private boolean _inView = false;
166     private _DeltaStateHelper _stateHelper = null;
167 
168     /**
169      * In JSF 2.0 bindings map was deprecated, and replaced with a map
170      * inside stateHelper. We need this one here because stateHelper needs
171      * to be implemented from here and internally it depends from this property.
172      */
173     private boolean _initialStateMarked = false;
174 
175     /** Value of the {@link UIComponent#HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME} parameter */
176     private Boolean _honorCurrentComponentAttributes;
177 
178     public UIComponent()
179     {
180     }
181 
182     public abstract Map<String, Object> getAttributes();
183     
184     /**
185      * @since 2.2
186      * @return 
187      */
188     public Map<String,Object> getPassThroughAttributes()
189     {
190         return getPassThroughAttributes(true);
191     }
192     
193     /**
194      * @since 2.2
195      * @param create
196      * @return A {@code Map} instance, or {@code null}.
197      */
198     public Map<String,Object> getPassThroughAttributes(boolean create)
199     {
200         return Collections.emptyMap();
201     }
202 
203     /**
204      *
205      * {@inheritDoc}
206      *
207      * @since 2.0
208      */
209     public boolean initialStateMarked()
210     {
211         return _initialStateMarked;
212     }
213 
214     /**
215      * Invokes the <code>invokeContextCallback</code> method with the component, specified by <code>clientId</code>.
216      *
217      * @param context
218      *            <code>FacesContext</code> for the current request
219      * @param clientId
220      *            the id of the desired <code>UIComponent</code> clazz
221      * @param callback
222      *            Implementation of the <code>ContextCallback</code> to be called
223      * @return has component been found ?
224      * @throws javax.faces.FacesException
225      */
226     public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
227             throws FacesException
228     {
229         // java.lang.NullPointerException - if any of the arguments are null
230         if (context == null || clientId == null || callback == null)
231         {
232             throw new NullPointerException();
233         }
234 
235         pushComponentToEL(context, this);
236         try
237         {
238             // searching for this component?
239             boolean found = clientId.equals(this.getClientId(context));
240             if (found)
241             {
242                 try
243                 {
244                     callback.invokeContextCallback(context, this);
245                 }
246                 catch (Exception e)
247                 {
248                     throw new FacesException(e);
249                 }
250                 return found;
251             }
252             // Searching for this component's children/facets
253             // [perf] Use getFacetsAndChildren() is nicer but this one prevents
254             // create 1 iterator per component class that does not have
255             // facets attached, which is a common case. 
256             if (this.getFacetCount() > 0)
257             {
258                 for (Iterator<UIComponent> it = this.getFacets().values().iterator(); !found && it.hasNext(); )
259                 {
260                     found = it.next().invokeOnComponent(context, clientId, callback);
261                 }                
262             }
263             if (this.getChildCount() > 0)
264             {
265                 for (int i = 0, childCount = getChildCount(); !found && (i < childCount); i++)
266                 {
267                     UIComponent child = getChildren().get(i);
268                     found = child.invokeOnComponent(context, clientId, callback);
269                 }
270             }
271             return found;
272         }
273         finally
274         {
275             //all components must call popComponentFromEl after visiting is finished
276             popComponentFromEL(context);
277         }
278     }
279 
280     /**
281      *
282      * @param component
283      * @return true if the component is a composite component otherwise false is returned
284      *
285      *
286      * @throws NullPointerException if the component is null
287      * @since 2.0
288      */
289     public static boolean isCompositeComponent(UIComponent component)
290     {
291 
292         //since _isCompositeComponent does it the same way we do it here also although I
293         //would prefer following method
294 
295         //return component.getRendererType().equals("javax.faces.Composite");
296 
297         return component.getAttributes().containsKey(Resource.COMPONENT_RESOURCE_KEY);
298     }
299 
300     /**
301      * Indicate if this component is inside a view,
302      * or in other words is contained by an UIViewRoot
303      * instance (which represents the view). If this component
304      * is a UIViewRoot instance, the components "always"
305      * is on the view.
306      *
307      * By default it is false but for UIViewRoot instances is
308      * true. 
309      *
310      * @return
311      *
312      * @since 2.0
313      */
314     public boolean isInView()
315     {
316         return _inView;
317     }
318 
319     public abstract boolean isRendered();
320 
321     public void markInitialState()
322     {
323         _initialStateMarked = true;
324     }
325 
326     /**
327      *
328      * This method indicates if a component is visitable
329      * according to the hints passed by the VisitContext parameter!
330      *
331      * This method internally is used by visitTree and if it returns false
332      * it short circuits the visitTree execution.
333      *
334      *
335      *
336      * @param context
337      * @return
338      *
339      * @since 2.0
340      */
341     protected boolean isVisitable(VisitContext context)
342     {
343 
344         Collection<VisitHint> hints = context.getHints();
345 
346         if (hints.contains(VisitHint.SKIP_TRANSIENT) && this.isTransient())
347         {
348             return false;
349         }
350 
351         if (hints.contains(VisitHint.SKIP_UNRENDERED) && !this.isRendered())
352         {
353             return false;
354         }
355 
356         //executable cannot be handled here because we do not have any method to determine
357         //whether a component is executable or not, this seems to be a hole in the spec!
358         //but we can resolve it on ppr context level, where it is needed!
359         //maybe in the long run we can move it down here, if it makes sense
360 
361         return true;
362     }
363 
364     /**
365      * @deprecated Replaced by setValueExpression
366      */
367     public abstract void setValueBinding(String name, ValueBinding binding);
368 
369     public void setValueExpression(String name, ValueExpression expression)
370     {
371         if (name == null)
372         {
373             throw new NullPointerException("name");
374         }
375         if (name.equals("id"))
376         {
377             throw new IllegalArgumentException("Can't set a ValueExpression for the 'id' property.");
378         }
379         if (name.equals("parent"))
380         {
381             throw new IllegalArgumentException("Can't set a ValueExpression for the 'parent' property.");
382         }
383 
384         if (expression == null)
385         {
386             //if (bindings != null) {
387             //    bindings.remove(name);
388             //    if (bindings.isEmpty()) {
389             //        bindings = null;
390             //    }
391             //}
392             getStateHelper().remove(PropertyKeys.bindings, name);
393         }
394         else
395         {
396             if (expression.isLiteralText())
397             {
398                 try
399                 {
400                     Object value = expression.getValue(getFacesContext().getELContext());
401                     getAttributes().put(name, value);
402                     return;
403                 }
404                 catch (ELException e)
405                 {
406                     throw new FacesException(e);
407                 }
408             }
409 
410             //if (bindings == null) {
411             //    bindings = new HashMap<String, ValueExpression>();
412             //}
413             //
414             //bindings.put(name, expression);
415             getStateHelper().put(PropertyKeys.bindings, name, expression);
416         }
417     }
418 
419     public String getClientId()
420     {
421         return getClientId(getFacesContext());
422     }
423 
424     public abstract String getClientId(FacesContext context);
425 
426     /**
427      * search for the nearest parent composite component, if no parent is found
428      * it has to return null!
429      *
430      * if the component itself is null we have to return null as well!
431      *
432      * @param component the component to start from
433      * @return the parent composite component if found otherwise null
434      *
435      * @since 2.0
436      */
437     public static UIComponent getCompositeComponentParent(UIComponent component)
438     {
439 
440         if (component == null)
441         {
442             return null;
443         }
444         UIComponent parent = component;
445 
446         do
447         {
448             parent = parent.getParent();
449             if (parent != null && UIComponent.isCompositeComponent(parent))
450             {
451                 return parent;
452             }
453         } while (parent != null);
454         return null;
455     }
456 
457     /**
458      * @since 1.2
459      */
460     public String getContainerClientId(FacesContext ctx)
461     {
462         if (ctx == null)
463         {
464             throw new NullPointerException("FacesContext ctx");
465         }
466 
467         return getClientId(ctx);
468     }
469 
470     /**
471      *
472      * @param context
473      * @return
474      *
475      * @since 2.0
476      */
477     public static UIComponent getCurrentComponent(FacesContext context)
478     {
479         Boolean honorCurrentComponentAttributes = null;
480 
481         if (context.getViewRoot() != null)
482         {
483             honorCurrentComponentAttributes = ((UIComponent)context.getViewRoot())._honorCurrentComponentAttributes;
484             if (honorCurrentComponentAttributes == null)
485             {
486                 honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
487             }
488         }
489         else
490         {
491             honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
492         }
493 
494         if (Boolean.TRUE.equals(honorCurrentComponentAttributes))
495         {
496             return (UIComponent) context.getAttributes().get(UIComponent.CURRENT_COMPONENT);
497         }
498         else
499         {
500             List<UIComponent> componentStack
501                     = (List<UIComponent>) context.getAttributes().get(UIComponent._COMPONENT_STACK);
502             if (componentStack == null)
503             {
504                 return null;
505             }
506             else
507             {
508                 if (componentStack.size() > 0)
509                 {
510                     return componentStack.get(componentStack.size()-1);
511                 }
512                 else
513                 {
514                     return null;
515                 }
516             }
517         }
518     }
519 
520     /**
521      *
522      * @param context
523      * @return
524      *
525      * @since 2.0
526      */
527     public static UIComponent getCurrentCompositeComponent(FacesContext context)
528     {
529         Boolean honorCurrentComponentAttributes = null;
530 
531         if (context.getViewRoot() != null)
532         {
533             honorCurrentComponentAttributes = ((UIComponent)context.getViewRoot())._honorCurrentComponentAttributes;
534             if (honorCurrentComponentAttributes == null)
535             {
536                 honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
537             }
538         }
539         else
540         {
541             honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
542         }
543 
544         if (Boolean.TRUE.equals(honorCurrentComponentAttributes))
545         {
546             return (UIComponent) context.getAttributes().get(UIComponent.CURRENT_COMPOSITE_COMPONENT);
547         }
548         else
549         {
550             return (UIComponent) context.getAttributes().get(UIComponent._CURRENT_COMPOSITE_COMPONENT_KEY);
551         }
552     }
553 
554     public abstract String getFamily();
555 
556     public abstract String getId();
557 
558     public List<SystemEventListener> getListenersForEventClass(Class<? extends SystemEvent> eventClass)
559     {
560         List<SystemEventListener> listeners;
561         if (_systemEventListenerClassMap == null)
562         {
563             listeners = Collections.emptyList();
564         }
565         else
566         {
567             listeners = _systemEventListenerClassMap.get(eventClass);
568             if (listeners == null)
569             {
570                 listeners = Collections.emptyList();
571             }
572             else
573             {
574                 listeners = Collections.unmodifiableList(listeners);
575             }
576         }
577 
578         return listeners;
579     }
580 
581     /**
582      *
583      * @return
584      *
585      * @since 2.0
586      */
587     public UIComponent getNamingContainer()
588     {
589         // Starting with "this", return the closest component in the ancestry that is a NamingContainer 
590         // or null if none can be found.
591         UIComponent component = this;
592         do
593         {
594             if (component instanceof NamingContainer)
595             {
596                 return component;
597             }
598 
599             component = component.getParent();
600         } while (component != null);
601 
602         return null;
603     }
604 
605     public abstract void setId(String id);
606 
607     /**
608      * Define if the component is on the view or not.
609      * <p>
610      * This value is set in the following conditions:
611      * </p>
612      * <ul>
613      * <li>Component / Facet added: if the parent isInView = true, 
614      *     set it to true and all their children or facets,
615      *     otherwise take no action</li>
616      * <li>Component / Facet removed: if the parent isInView = false,
617      *     set it to false and all their children or facets,
618      *     otherwise take no action</li>
619      * </ul>
620      * @param isInView
621      *
622      * @since 2.0
623      */
624     public void setInView(boolean isInView)
625     {
626         _inView = isInView;
627     }
628 
629     /**
630      * For JSF-framework internal use only. Don't call this method to add components to the component tree. Use
631      * <code>parent.getChildren().add(child)</code> instead.
632      */
633     public abstract void setParent(UIComponent parent);
634 
635     /**
636      * Returns the parent of the component. Children can be added to or removed from a component even if this method
637      * returns null for the child.
638      */
639     public abstract UIComponent getParent();
640 
641     public abstract void setRendered(boolean rendered);
642 
643     public abstract String getRendererType();
644 
645     public abstract void setRendererType(String rendererType);
646 
647     public abstract boolean getRendersChildren();
648 
649     public Map<String, String> getResourceBundleMap()
650     {
651         if (_resourceBundleMap == null)
652         {
653             FacesContext context = getFacesContext();
654             Locale locale = context.getViewRoot().getLocale();
655             ClassLoader loader = _ClassUtils.getContextClassLoader();
656 
657             try
658             {
659                 // looks for a ResourceBundle with a base name equal to the fully qualified class
660                 // name of the current UIComponent this and Locale equal to the Locale of the current UIViewRoot.
661                 _resourceBundleMap = new BundleMap(ResourceBundle.getBundle(getClass().getName(), locale, loader));
662             }
663             catch (MissingResourceException e)
664             {
665                 // If no such bundle is found, and the component is a composite component
666                 if (this._isCompositeComponent())
667                 {
668                     // No need to check componentResource (the resource used to build the composite
669                     // component instance) to null since it is already done on this._isCompositeComponent()
670                     Resource componentResource = (Resource) getAttributes().get(Resource.COMPONENT_RESOURCE_KEY);
671                     // Let resourceName be the resourceName of the Resource for this composite component,
672                     // replacing the file extension with ".properties"
673                     int extensionIndex = componentResource.getResourceName().lastIndexOf('.');
674                     String resourceName = (extensionIndex < 0
675                             ? componentResource.getResourceName()
676                             : componentResource.getResourceName().substring(0, extensionIndex)) + ".properties";
677 
678                     // Let libraryName be the libraryName of the the Resource for this composite component.
679                     // Call ResourceHandler.createResource(java.lang.String,java.lang.String), passing the derived
680                     // resourceName and
681                     // libraryName.
682                     Resource bundleResource = context.getApplication().getResourceHandler()
683                             .createResource(resourceName, componentResource.getLibraryName());
684 
685                     if (bundleResource != null)
686                     {
687                         // If the resultant Resource exists and can be found, the InputStream for the resource
688                         // is used to create a ResourceBundle. If either of the two previous steps for obtaining the
689                         // ResourceBundle
690                         // for this component is successful, the ResourceBundle is wrapped in a Map<String, String> and
691                         // returned.
692                         try
693                         {
694                             _resourceBundleMap
695                                     = new BundleMap(new PropertyResourceBundle(bundleResource.getInputStream()));
696                         }
697                         catch (IOException e1)
698                         {
699                             // Nothing happens, then resourceBundleMap is set as empty map
700                         }
701                     }
702                 }
703                 // Otherwise Collections.EMPTY_MAP is returned.
704                 if (_resourceBundleMap == null)
705                 {
706                     _resourceBundleMap = Collections.emptyMap();
707                 }
708             }
709         }
710 
711         return _resourceBundleMap;
712     }
713 
714     /**
715      * @deprecated Replaced by getValueExpression
716      */
717     public abstract ValueBinding getValueBinding(String name);
718 
719     public ValueExpression getValueExpression(String name)
720     {
721         if (name == null)
722         {
723             throw new NullPointerException("name can not be null");
724         }
725 
726         Map<String, Object> bindings = (Map<String, Object>) getStateHelper().
727                 get(PropertyKeys.bindings);
728 
729         if (bindings == null)
730         {
731             if (!(this instanceof UIComponentBase))
732             {
733                 // if the component does not inherit from UIComponentBase and don't implements JSF 1.2 or later
734                 ValueBinding vb = getValueBinding(name);
735                 if (vb != null)
736                 {
737                     //bindings = new HashMap<String, ValueExpression>();
738                     ValueExpression ve = new _ValueBindingToValueExpression(vb);
739                     getStateHelper().put(PropertyKeys.bindings, name, ve);
740                     return ve;
741                 }
742             }
743         }
744         else
745         {
746             //return bindings.get(name);
747             return (ValueExpression) bindings.get(name);
748         }
749         return null;
750     }
751 
752     public abstract List<UIComponent> getChildren();
753 
754     public abstract int getChildCount();
755 
756     public abstract UIComponent findComponent(String expr);
757 
758     public abstract Map<String, UIComponent> getFacets();
759 
760     public abstract UIComponent getFacet(String name);
761 
762     public abstract Iterator<UIComponent> getFacetsAndChildren();
763 
764     public abstract void broadcast(FacesEvent event) throws AbortProcessingException;
765 
766     /**
767      * {@inheritDoc}
768      *
769      * @since 2.0
770      */
771     public void clearInitialState()
772     {
773         _initialStateMarked = false;
774     }
775 
776     public abstract void decode(FacesContext context);
777 
778     public abstract void encodeBegin(FacesContext context) throws IOException;
779 
780     public abstract void encodeChildren(FacesContext context) throws IOException;
781 
782     public abstract void encodeEnd(FacesContext context) throws IOException;
783 
784     public void encodeAll(FacesContext context) throws IOException
785     {
786         if (context == null)
787         {
788             throw new NullPointerException();
789         }
790 
791         pushComponentToEL(context, this);
792         try
793         {
794             if (!isRendered())
795             {
796                 return;
797             }
798         }
799         finally
800         {
801             popComponentFromEL(context);
802         }
803 
804         //if (isRendered()) {
805         this.encodeBegin(context);
806 
807         // rendering children
808         if (this.getRendersChildren())
809         {
810             this.encodeChildren(context);
811         } // let children render itself
812         else
813         {
814             if (this.getChildCount() > 0)
815             {
816                 for (int i = 0; i < this.getChildCount(); i++)
817                 {
818                     UIComponent comp = this.getChildren().get(i);
819                     comp.encodeAll(context);
820                 }
821             }
822         }
823         this.encodeEnd(context);
824         //}
825     }
826 
827     protected abstract void addFacesListener(FacesListener listener);
828 
829     protected abstract FacesListener[] getFacesListeners(Class clazz);
830 
831     protected abstract void removeFacesListener(FacesListener listener);
832 
833     public abstract void queueEvent(FacesEvent event);
834 
835     public abstract void processRestoreState(FacesContext context, Object state);
836 
837     public abstract void processDecodes(FacesContext context);
838 
839     public void processEvent(ComponentSystemEvent event) throws AbortProcessingException
840     {
841         // The default implementation performs the following action. If the argument event is an instance of
842         // AfterRestoreStateEvent,
843         if (event instanceof PostRestoreStateEvent)
844         {
845 
846             // call this.getValueExpression(java.lang.String) passing the literal string "binding"
847             ValueExpression expression = getValueExpression("binding");
848 
849             // If the result is non-null, set the value of the ValueExpression to be this.
850             if (expression != null)
851             {
852                 expression.setValue(getFacesContext().getELContext(), this);
853             }
854 
855             //we issue a PostRestoreStateEvent
856             //we issue it here because the spec clearly states what UIComponent is allowed to do
857             //the main issue is that the spec does not say anything about a global dispatch on this level
858             //but a quick blackbox test against the ri revealed that the event clearly is dispatched
859             //at restore level for every component so we either issue it here or in UIViewRoot and/or the facelet
860             // and jsp restore state triggers, a central point is preferrble so we do it here
861             //TODO ask the EG the spec clearly contradicts blackbox RI behavior here 
862 
863             //getFacesContext().getApplication().publishEvent(getFacesContext(),
864             // PostRestoreStateEvent.class, UIComponent.class, this);
865             
866             // JSF 2.2 vdl.createComponent() requires special handling to refresh
867             // dynamic parts when refreshing is done. The only way to do it is 
868             // attaching a listener to PostRestoreStateEvent, so we need to do this
869             // invocation here.
870             // Do it inside UIComponent.processEvent() is better because in facelets
871             // UILeaf we can skip this part just overriding the method.
872             
873             List<SystemEventListener> listeners = this.getListenersForEventClass(
874                 PostRestoreStateEvent.class);
875             if (!listeners.isEmpty())
876             {
877                 for (int i  = 0, size = listeners.size(); i < size; i++)
878                 {
879                     SystemEventListener listener = listeners.get(i);
880                     if (listener.isListenerForSource(this))
881                     {
882                         // Check if the listener points again to the component, to
883                         // avoid StackoverflowException
884                         boolean shouldProcessEvent = true;
885                         if (listener instanceof EventListenerWrapper && 
886                             ((EventListenerWrapper)listener).listenerCapability == 
887                                 EventListenerWrapper.LISTENER_TYPE_COMPONENT)
888                         {
889                             shouldProcessEvent = false;
890                         }
891                         if (shouldProcessEvent)
892                         {
893                             listener.processEvent(event);
894                         }
895                     }
896                 }
897             }
898         }
899     }
900 
901     public abstract void processValidators(FacesContext context);
902 
903     public abstract void processUpdates(FacesContext context);
904 
905     public abstract java.lang.Object processSaveState(FacesContext context);
906 
907     public void subscribeToEvent(Class<? extends SystemEvent> eventClass,
908                                  ComponentSystemEventListener componentListener)
909     {
910         // The default implementation creates an inner SystemEventListener instance that wraps argument
911         // componentListener as the listener argument.
912         if (eventClass == null)
913         {
914             throw new NullPointerException("eventClass required");
915         }
916         if (componentListener == null)
917         {
918             throw new NullPointerException("componentListener required");
919         }
920 
921         SystemEventListener listener = new EventListenerWrapper(this, componentListener);
922 
923         // Make sure the map exists
924         if (_systemEventListenerClassMap == null)
925         {
926             _systemEventListenerClassMap = new HashMap<Class<? extends SystemEvent>, List<SystemEventListener>>();
927         }
928 
929         List<SystemEventListener> listeners = _systemEventListenerClassMap.get(eventClass);
930         // Make sure the list for class exists
931         if (listeners == null)
932         {
933             // how many listeners per event type can single component have? 
934             // We use 3 here as expected number, but it is a question 
935             listeners = new _DeltaList<SystemEventListener>(3);
936             _systemEventListenerClassMap.put(eventClass, listeners);
937         }
938 
939         // Deal with contains? Spec is silent
940         listeners.add(listener);
941     }
942 
943     public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass,
944                                      ComponentSystemEventListener componentListener)
945     {
946         /*
947          * When doing the comparison to determine if an existing listener is equal to the argument componentListener
948          * (and thus must be removed), the equals() method on the existing listener must be invoked, passing the
949          * argument componentListener, rather than the other way around.
950          * 
951          * -=Simon Lessard=- What is that supposed to mean? Are we supposed to keep
952          * an internal map of created listener wrappers?
953          * -= Leonardo Uribe=- Yes, it is supposed a wrapper should be used to hold listener references, to prevent
954          * serialize component instances on the state.
955          */
956         if (eventClass == null)
957         {
958             throw new NullPointerException("eventClass required");
959         }
960         if (componentListener == null)
961         {
962             throw new NullPointerException("componentListener required");
963         }
964 
965         if (_systemEventListenerClassMap != null)
966         {
967             List<SystemEventListener> listeners = _systemEventListenerClassMap.get(eventClass);
968 
969             if (listeners != null && !listeners.isEmpty())
970             {
971                 for (Iterator<SystemEventListener> it = listeners.iterator(); it.hasNext(); )
972                 {
973                     ComponentSystemEventListener listener
974                             = ((EventListenerWrapper) it.next()).getComponentSystemEventListener();
975                     if (listener != null && listener.equals(componentListener))
976                     {
977                         it.remove();
978                         break;
979                     }
980                 }
981             }
982         }
983     }
984 
985     /**
986      * The visit tree method, visit tree walks over a subtree and processes
987      * the callback object to perform some operation on the subtree
988      * <p>
989      * there are some details in the implementation which according to the spec have
990      * to be in place:
991      * a) before calling the callback and traversing into the subtree  pushComponentToEL
992      * has to be called
993      * b) after the processing popComponentFromEL has to be performed to remove the component
994      * from the el
995      * </p>
996      * <p>
997      * The tree traversal optimizations are located in the visit context and can be replaced
998      * via the VisitContextFactory in the faces-config factory section
999      * </p>
1000      *
1001      * @param context the visit context which handles the processing details
1002      * @param callback the callback to be performed
1003      * @return false if the processing is not done true if we can shortcut
1004      * the visiting because we are done with everything
1005      *
1006      * @since 2.0
1007      */
1008     public boolean visitTree(VisitContext context, VisitCallback callback)
1009     {
1010         try
1011         {
1012             pushComponentToEL(context.getFacesContext(), this);
1013 
1014             if (!isVisitable(context))
1015             {
1016                 return false;
1017             }
1018 
1019             VisitResult res = context.invokeVisitCallback(this, callback);
1020             switch (res)
1021             {
1022                 //we are done nothing has to be processed anymore
1023                 case COMPLETE:
1024                     return true;
1025 
1026                 case REJECT:
1027                     return false;
1028 
1029                 //accept
1030                 default:
1031                     if (getFacetCount() > 0)
1032                     {
1033                         for (UIComponent facet : getFacets().values())
1034                         {
1035                             if (facet.visitTree(context, callback))
1036                             {
1037                                 return true;
1038                             }
1039                         }
1040                     }
1041                     int childCount = getChildCount();
1042                     if (childCount > 0)
1043                     {
1044                         for (int i = 0; i < childCount; i++)
1045                         {
1046                             UIComponent child = getChildren().get(i);
1047                             if (child.visitTree(context, callback))
1048                             {
1049                                 return true;
1050                             }
1051                         }
1052                     }
1053                     return false;
1054             }
1055         }
1056         finally
1057         {
1058             //all components must call popComponentFromEl after visiting is finished
1059             popComponentFromEL(context.getFacesContext());
1060         }
1061     }
1062 
1063     protected abstract FacesContext getFacesContext();
1064 
1065     protected abstract Renderer getRenderer(FacesContext context);
1066 
1067     /**
1068      * Note that id, clientId properties
1069      * never change its value after the component is populated,
1070      * so we don't need to store it on StateHelper or restore it when
1071      * initialStateMarked == true
1072      * (Note that rendererType is suspicious, in theory this field is
1073      * initialized on constructor, but on 1.1 and 1.2 is saved and restored,
1074      * so to keep backward behavior we put it on StateHelper )
1075      *
1076      * Also, facesListeners can't be wrapped on StateHelper because it
1077      * needs to handle PartialStateHolder instances when it is saved and
1078      * restored and this interface does not implement PartialStateHolder,
1079      * so we can't propagate calls to markInitialState and clearInitialState,
1080      * in other words, the List wrapped by StateHelper does not handle
1081      * PartialStateHolder items.
1082      *
1083      * "bindings" map does not need to deal with PartialStateHolder instances,
1084      *  so we can use StateHelper feature (handle delta for this map or in
1085      *  other words track add/removal from bindings map as delta).
1086      */
1087     enum PropertyKeys
1088     {
1089         rendered,
1090         rendererType,
1091         attributesMap,
1092         bindings,
1093         facesListeners,
1094         passThroughAttributesMap
1095     }
1096 
1097     protected StateHelper getStateHelper()
1098     {
1099         return getStateHelper(true);
1100     }
1101 
1102     /**
1103      * returns a delta state saving enabled state helper
1104      * for the current component
1105      * @param create if true a state helper is created if not already existing
1106      * @return an implementation of the StateHelper interface or null if none exists and create is set to false
1107      */
1108     protected StateHelper getStateHelper(boolean create)
1109     {
1110         if (_stateHelper != null)
1111         {
1112             return _stateHelper;
1113         }
1114         if (create)
1115         {
1116             _stateHelper = new _DeltaStateHelper(this);
1117         }
1118         return _stateHelper;
1119     }
1120 
1121     public TransientStateHelper getTransientStateHelper()
1122     {
1123         return getTransientStateHelper(true);
1124     }
1125 
1126     public TransientStateHelper getTransientStateHelper(boolean create)
1127     {
1128         if (_stateHelper != null)
1129         {
1130             return _stateHelper;
1131         }
1132         if (create)
1133         {
1134             _stateHelper = new _DeltaStateHelper(this);
1135         }
1136         return _stateHelper;
1137     }
1138 
1139     public void restoreTransientState(FacesContext context, Object state)
1140     {
1141         getTransientStateHelper().restoreTransientState(context, state);
1142     }
1143 
1144     public Object saveTransientState(FacesContext context)
1145     {
1146         return getTransientStateHelper().saveTransientState(context);
1147     }
1148 
1149     @SuppressWarnings("unchecked")
1150     public void popComponentFromEL(FacesContext context)
1151     {
1152         Map<Object, Object> contextAttributes = context.getAttributes();
1153 
1154         if (_honorCurrentComponentAttributes == null)
1155         {
1156             _honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
1157         }
1158 
1159         if (Boolean.TRUE.equals(_honorCurrentComponentAttributes))
1160         {
1161             // Pop the current UIComponent from the FacesContext attributes map so that the previous 
1162             // UIComponent, if any, becomes the current component.
1163             List<UIComponent> componentStack
1164                     = (List<UIComponent>) contextAttributes.get(UIComponent._COMPONENT_STACK);
1165 
1166             UIComponent oldCurrent = (UIComponent) contextAttributes.get(UIComponent.CURRENT_COMPONENT);
1167 
1168             UIComponent newCurrent = null;
1169             if (componentStack != null && !componentStack.isEmpty())
1170             {
1171                 if (!this.equals(oldCurrent))
1172                 {
1173                     //Check on the componentStack if it can be found
1174                     int componentIndex = componentStack.lastIndexOf(this);
1175                     if (componentIndex >= 0)
1176                     {
1177                         //for (int i = 0; i < (componentIndex + 1); i++)
1178                         for (int i = componentStack.size()-1; i >= componentIndex ; i--)
1179                         {
1180                             newCurrent = componentStack.remove(componentStack.size()-1);
1181                         }
1182                     }
1183                     else
1184                     {
1185                         //Component not found on the stack. Do not pop.
1186                         return;
1187                     }
1188                 }
1189                 else
1190                 {
1191                     newCurrent = componentStack.remove(componentStack.size()-1);
1192                 }
1193             }
1194             else
1195             {
1196                 //Reset the current composite component
1197                 contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, null);
1198             }
1199             oldCurrent = (UIComponent) contextAttributes.put(UIComponent.CURRENT_COMPONENT, newCurrent);
1200 
1201             if (oldCurrent != null && oldCurrent._isCompositeComponent() && newCurrent != null)
1202             {
1203                 // Recalculate the current composite component
1204                 if (newCurrent._isCompositeComponent())
1205                 {
1206                     contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, newCurrent);
1207                 }
1208                 else
1209                 {
1210                     UIComponent previousCompositeComponent = null;
1211                     for (int i = componentStack.size()-1; i >= 0; i--)
1212                     {
1213                         UIComponent component = componentStack.get(i);
1214                         if (component._isCompositeComponent())
1215                         {
1216                             previousCompositeComponent = component;
1217                             break;
1218                         }
1219                     }
1220                     contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, previousCompositeComponent);
1221                 }
1222             }
1223         }
1224         else
1225         {
1226             // Pop the current UIComponent from the FacesContext attributes map so that the previous 
1227             // UIComponent, if any, becomes the current component.
1228             List<UIComponent> componentStack
1229                     = (List<UIComponent>) contextAttributes.get(UIComponent._COMPONENT_STACK);
1230 
1231             UIComponent oldCurrent = null;
1232             if (componentStack != null && !componentStack.isEmpty())
1233             {
1234                 int componentIndex = componentStack.lastIndexOf(this);
1235                 if (componentIndex >= 0)
1236                 {
1237                     for (int i = componentStack.size()-1; i >= componentIndex ; i--)
1238                     {
1239                         oldCurrent = componentStack.remove(componentStack.size()-1);
1240                     }
1241                 }
1242                 else
1243                 {
1244                     return;
1245                 }
1246             }
1247 
1248             if (oldCurrent != null && oldCurrent._isCompositeComponent())
1249             {
1250                 // Recalculate the current composite component
1251                 UIComponent previousCompositeComponent = null;
1252                 for (int i = componentStack.size()-1; i >= 0; i--)
1253                 {
1254                     UIComponent component = componentStack.get(i);
1255                     if (component._isCompositeComponent())
1256                     {
1257                         previousCompositeComponent = component;
1258                         break;
1259                     }
1260                 }
1261                 contextAttributes.put(UIComponent._CURRENT_COMPOSITE_COMPONENT_KEY, previousCompositeComponent);
1262             }
1263         }
1264     }
1265 
1266     @SuppressWarnings("unchecked")
1267     public void pushComponentToEL(FacesContext context, UIComponent component)
1268     {
1269         if (component == null)
1270         {
1271             component = this;
1272         }
1273 
1274         Map<Object, Object> contextAttributes = context.getAttributes();
1275 
1276         if (_honorCurrentComponentAttributes == null)
1277         {
1278             _honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
1279         }
1280 
1281         if (Boolean.TRUE.equals(_honorCurrentComponentAttributes))
1282         {
1283             UIComponent currentComponent = (UIComponent) contextAttributes.get(UIComponent.CURRENT_COMPONENT);
1284 
1285             if (currentComponent != null)
1286             {
1287                 List<UIComponent> componentStack
1288                         = (List<UIComponent>) contextAttributes.get(UIComponent._COMPONENT_STACK);
1289                 if (componentStack == null)
1290                 {
1291                     componentStack = new ArrayList<UIComponent>();
1292                     contextAttributes.put(UIComponent._COMPONENT_STACK, componentStack);
1293                 }
1294 
1295                 componentStack.add(currentComponent);
1296             }
1297 
1298             // Push the current UIComponent this to the FacesContext  attribute map using the key CURRENT_COMPONENT 
1299             // saving the previous UIComponent associated with CURRENT_COMPONENT for a subsequent call to 
1300             // popComponentFromEL(javax.faces.context.FacesContext).
1301             contextAttributes.put(UIComponent.CURRENT_COMPONENT, component);
1302 
1303             if (component._isCompositeComponent())
1304             {
1305                 contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, component);
1306             }
1307         }
1308         else
1309         {
1310             List<UIComponent> componentStack
1311                     = (List<UIComponent>) contextAttributes.get(UIComponent._COMPONENT_STACK);
1312             if (componentStack == null)
1313             {
1314                 componentStack = new ArrayList<UIComponent>();
1315                 contextAttributes.put(UIComponent._COMPONENT_STACK, componentStack);
1316             }
1317             componentStack.add(component);
1318             if (component._isCompositeComponent())
1319             {
1320                 contextAttributes.put(UIComponent._CURRENT_COMPOSITE_COMPONENT_KEY, component);
1321             }
1322         }
1323     }
1324 
1325     /**
1326      * @since 1.2
1327      */
1328     public int getFacetCount()
1329     {
1330         // not sure why the RI has this method in both
1331         // UIComponent and UIComponentBase
1332         Map<String, UIComponent> facets = getFacets();
1333         return facets == null ? 0 : facets.size();
1334     }
1335 
1336     private boolean _isCompositeComponent()
1337     {
1338         //moved to the static method
1339         return UIComponent.isCompositeComponent(this);
1340     }
1341     
1342     boolean isCachedFacesContext()
1343     {
1344         return false;
1345     }
1346 
1347     // Dummy method to prevent cast for UIComponentBase when caching
1348     void setCachedFacesContext(FacesContext facesContext)
1349     {
1350     }
1351 
1352     /**
1353      * Gets value of "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES" parameter cached in facesContext.attributes 
1354      * or resolves that param and caches its value in facesContext.attributes.    
1355      *
1356      * @return canonical Boolean value for parameter "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES"
1357      */
1358     private static Boolean _getHonorCurrentComponentAttributes(FacesContext facesContext)
1359     {
1360         // performance note: we cache value in facesContext.attributes because
1361         // 1) methods pushComponentToEL, popComponentFromEl, getCurrentComponent a getCurrentCompositeComponent
1362         // can use that value
1363         // 2) getExternalContext().getInitParameter has undetermined performance. In typical JSF app, there
1364         // are one or two wrappers around external context; servletContext.getInitParameter has also unknown 
1365         // implementation and performance
1366         Map<Object, Object> attributes = facesContext.getAttributes();
1367         Boolean paramValue = (Boolean) attributes.get(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME);
1368         if (paramValue == null)
1369         {
1370             String param
1371                     = facesContext.getExternalContext().getInitParameter(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME);
1372             paramValue = Boolean.valueOf((param != null && Boolean.valueOf(param).booleanValue()));
1373             attributes.put(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME, paramValue);
1374         }
1375         return paramValue;
1376     }
1377 
1378     private static class BundleMap implements Map<String, String>
1379     {
1380 
1381         private ResourceBundle _bundle;
1382         private List<String> _values;
1383 
1384         public BundleMap(ResourceBundle bundle)
1385         {
1386             _bundle = bundle;
1387         }
1388 
1389         // Optimized methods
1390         public String get(Object key)
1391         {
1392             try
1393             {
1394                 return (String) _bundle.getObject(key.toString());
1395             }
1396             catch (Exception e)
1397             {
1398                 return "???" + key + "???";
1399             }
1400         }
1401 
1402         public boolean isEmpty()
1403         {
1404             return !_bundle.getKeys().hasMoreElements();
1405         }
1406 
1407         public boolean containsKey(Object key)
1408         {
1409             try
1410             {
1411                 return _bundle.getObject(key.toString()) != null;
1412             }
1413             catch (MissingResourceException e)
1414             {
1415                 return false;
1416             }
1417         }
1418 
1419         // Unoptimized methods
1420         public Collection<String> values()
1421         {
1422             if (_values == null)
1423             {
1424                 _values = new ArrayList<String>();
1425                 for (Enumeration<String> enumer = _bundle.getKeys(); enumer.hasMoreElements(); )
1426                 {
1427                     String v = _bundle.getString(enumer.nextElement());
1428                     _values.add(v);
1429                 }
1430             }
1431             return _values;
1432         }
1433 
1434         public int size()
1435         {
1436             return values().size();
1437         }
1438 
1439         public boolean containsValue(Object value)
1440         {
1441             return values().contains(value);
1442         }
1443 
1444         public Set<Map.Entry<String, String>> entrySet()
1445         {
1446             Set<Entry<String, String>> set = new HashSet<Entry<String, String>>();
1447             for (Enumeration<String> enumer = _bundle.getKeys(); enumer.hasMoreElements(); )
1448             {
1449                 final String k = enumer.nextElement();
1450                 set.add(new Map.Entry<String, String>()
1451                 {
1452 
1453                     public String getKey()
1454                     {
1455                         return k;
1456                     }
1457 
1458                     public String getValue()
1459                     {
1460                         return (String) _bundle.getObject(k);
1461                     }
1462 
1463                     public String setValue(String value)
1464                     {
1465                         throw new UnsupportedOperationException();
1466                     }
1467                 });
1468             }
1469 
1470             return set;
1471         }
1472 
1473         public Set<String> keySet()
1474         {
1475             Set<String> set = new HashSet<String>();
1476             for (Enumeration<String> enumer = _bundle.getKeys(); enumer.hasMoreElements(); )
1477             {
1478                 set.add(enumer.nextElement());
1479             }
1480             return set;
1481         }
1482 
1483         // Unsupported methods
1484         public String remove(Object key)
1485         {
1486             throw new UnsupportedOperationException();
1487         }
1488 
1489         public void putAll(Map<? extends String, ? extends String> t)
1490         {
1491             throw new UnsupportedOperationException();
1492         }
1493 
1494         public String put(String key, String value)
1495         {
1496             throw new UnsupportedOperationException();
1497         }
1498 
1499         public void clear()
1500         {
1501             throw new UnsupportedOperationException();
1502         }
1503     }
1504 
1505     static class EventListenerWrapper implements SystemEventListener, PartialStateHolder
1506     {
1507 
1508         private Class<?> componentClass;
1509         private ComponentSystemEventListener listener;
1510 
1511         private boolean _initialStateMarked;
1512 
1513         private int listenerCapability;
1514         private transient UIComponent _component;
1515 
1516         private static final int LISTENER_SAVE_STATE_HOLDER = 1;
1517         private static final int LISTENER_SAVE_PARTIAL_STATE_HOLDER = 2;
1518         private static final int LISTENER_TYPE_COMPONENT = 4;
1519         private static final int LISTENER_TYPE_RENDERER = 8;
1520         private static final int LISTENER_TYPE_OTHER = 16;
1521 
1522         public EventListenerWrapper()
1523         {
1524             //need a no-arg constructor for state saving purposes
1525             super();
1526         }
1527 
1528         /**
1529          * Note we have two cases:
1530          *
1531          * 1. listener is an instance of UIComponent. In this case we cannot save and restore
1532          *    it because we need to point to the real component, but we can assume the instance
1533          *    is the same because UIComponent.subscribeToEvent says so. Also take into account
1534          *    this case is the reason why we need a wrapper for UIComponent.subscribeToEvent
1535          * 2. listener is an instance of Renderer. In this case we can assume the same renderer
1536          *    used by the source component is the one used by the listener (ListenerFor). 
1537          * 3. listener is an instance of ComponentSystemEventListener but not from UIComponent.
1538          *    In this case, the instance could implement StateHolder, PartialStateHolder or do
1539          *    implement anything, so we have to deal with that case as usual.
1540          *
1541          * @param component
1542          * @param listener
1543          */
1544         public EventListenerWrapper(UIComponent component, ComponentSystemEventListener listener)
1545         {
1546             assert component != null;
1547             assert listener != null;
1548 
1549             this.componentClass = component.getClass();
1550             this.listener = listener;
1551             this._component = component;
1552             initListenerCapability();
1553         }
1554 
1555         private void initListenerCapability()
1556         {
1557             this.listenerCapability = 0;
1558             if (this.listener instanceof UIComponent)
1559             {
1560                 this.listenerCapability = LISTENER_TYPE_COMPONENT;
1561             }
1562             else if (this.listener instanceof Renderer)
1563             {
1564                 this.listenerCapability = LISTENER_TYPE_RENDERER;
1565             }
1566             else
1567             {
1568                 if (this.listener instanceof PartialStateHolder)
1569                 {
1570                     this.listenerCapability = LISTENER_TYPE_OTHER | LISTENER_SAVE_PARTIAL_STATE_HOLDER;
1571                 }
1572                 else if (this.listener instanceof StateHolder)
1573                 {
1574                     this.listenerCapability = LISTENER_TYPE_OTHER | LISTENER_SAVE_STATE_HOLDER;
1575                 }
1576                 else
1577                 {
1578                     this.listenerCapability = LISTENER_TYPE_OTHER;
1579                 }
1580             }
1581         }
1582 
1583         @Override
1584         public boolean equals(Object o)
1585         {
1586             if (o == this)
1587             {
1588                 return true;
1589             }
1590             else if (o instanceof EventListenerWrapper)
1591             {
1592                 EventListenerWrapper other = (EventListenerWrapper) o;
1593                 return componentClass.equals(other.componentClass) && listener.equals(other.listener);
1594             }
1595             else
1596             {
1597                 return false;
1598             }
1599         }
1600 
1601         @Override
1602         public int hashCode()
1603         {
1604             return componentClass.hashCode() + listener.hashCode();
1605         }
1606 
1607         public boolean isListenerForSource(Object source)
1608         {
1609             // and its implementation of SystemEventListener.isListenerForSource(java.lang.Object) must return true
1610             // if the instance class of this UIComponent is assignable from the argument to isListenerForSource.
1611 
1612             return source.getClass().isAssignableFrom(componentClass);
1613         }
1614 
1615         public ComponentSystemEventListener getComponentSystemEventListener()
1616         {
1617             return listener;
1618         }
1619 
1620         public void processEvent(SystemEvent event)
1621         {
1622             // This inner class must call through to the argument componentListener in its implementation of
1623             // SystemEventListener.processEvent(javax.faces.event.SystemEvent)
1624 
1625             assert event instanceof ComponentSystemEvent;
1626 
1627             listener.processEvent((ComponentSystemEvent) event);
1628         }
1629 
1630         public void clearInitialState()
1631         {
1632             //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
1633             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
1634             {
1635                 ((PartialStateHolder) listener).clearInitialState();
1636             }
1637             _initialStateMarked = false;
1638         }
1639 
1640         public boolean initialStateMarked()
1641         {
1642             //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
1643             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
1644             {
1645                 return ((PartialStateHolder) listener).initialStateMarked();
1646             }
1647             //return false;
1648             return _initialStateMarked;
1649         }
1650 
1651         public void markInitialState()
1652         {
1653             //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
1654             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
1655             {
1656                 ((PartialStateHolder) listener).markInitialState();
1657             }
1658             _initialStateMarked = true;
1659         }
1660 
1661         public boolean isTransient()
1662         {
1663             //if ( listener instanceof StateHolder)
1664             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 ||
1665                     (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0)
1666             {
1667                 return ((StateHolder) listener).isTransient();
1668             }
1669             return false;
1670         }
1671 
1672         public void restoreState(FacesContext context, Object state)
1673         {
1674             if (state == null)
1675             {
1676                 return;
1677             }
1678             Object[] values = (Object[]) state;
1679             componentClass = (Class) values[0];
1680             if (values[1] instanceof _AttachedDeltaWrapper)
1681             {
1682                 ((StateHolder) listener).restoreState(context,
1683                         ((_AttachedDeltaWrapper) values[1]).getWrappedStateObject());
1684             }
1685             else
1686             {
1687                 //Full restore
1688                 listenerCapability = (Integer) values[2];
1689 
1690                 _component = UIComponent.getCurrentComponent(context);
1691                 if ((listenerCapability & LISTENER_TYPE_COMPONENT) != 0)
1692                 {
1693                     listener = _component;
1694                 }
1695                 else if ((listenerCapability & LISTENER_TYPE_RENDERER) != 0)
1696                 {
1697                     //listener = (ComponentSystemEventListener)
1698                     //        UIComponent.getCurrentComponent(context).getRenderer(context);
1699                     Renderer renderer = _component.getRenderer(context);
1700                     Integer i = (Integer) values[1];
1701                     if (i != null && i >= 0)
1702                     {
1703                         while (i > 0)
1704                         {
1705                             renderer = ((RendererWrapper) renderer).getWrapped();
1706                             i--;
1707                         }
1708                     }
1709                     listener = (ComponentSystemEventListener) renderer;
1710                 }
1711                 else
1712                 {
1713                     listener = (ComponentSystemEventListener)
1714                             UIComponentBase.restoreAttachedState(context, values[1]);
1715                 }
1716                 /*
1717                 listener = values[1] == null ? 
1718                         UIComponent.getCurrentComponent(context) : 
1719                             (ComponentSystemEventListener) UIComponentBase.restoreAttachedState(context, values[1]);
1720                             */
1721             }
1722         }
1723 
1724         public Object saveState(FacesContext context)
1725         {
1726             if (!initialStateMarked())
1727             {
1728                 /*
1729                 Object[] state = new Object[2];
1730                 state[0] = componentClass;
1731                 if (!(listener instanceof UIComponent))
1732                 {
1733                     state[1] = UIComponentBase.saveAttachedState(context, listener);
1734                 }
1735                 return state;
1736                 */
1737                 Object[] state = new Object[3];
1738                 state[0] = componentClass;
1739                 //If this is not a component or a renderer, save it calling UIComponent.saveAttachedState
1740                 if (!((listenerCapability & LISTENER_TYPE_COMPONENT) != 0 ||
1741                         (listenerCapability & LISTENER_TYPE_RENDERER) != 0))
1742                 {
1743                     state[1] = UIComponentBase.saveAttachedState(context, listener);
1744                 }
1745                 else
1746                 {
1747                     if ( (listenerCapability & LISTENER_TYPE_RENDERER) != 0)
1748                     {
1749                         UIComponent componentRef = _component != null ? _component : getCurrentComponent(context);
1750                         Renderer renderer = componentRef.getRenderer(context);
1751                         int i = 0;
1752                         while (renderer != null && !renderer.getClass().equals(listener.getClass()))
1753                         {
1754                             if (renderer instanceof RendererWrapper)
1755                             {
1756                                 renderer = ((RendererWrapper) renderer).getWrapped();
1757                                 i++;
1758                             }
1759                             else
1760                             {
1761                                 renderer = null;
1762                                 i = -1;
1763                             }
1764                         }
1765                         if (i != -1)
1766                         {
1767                             // Store the number so we can get the right wrapper to invoke the method.
1768                             state[1] = i;
1769                         }
1770                         else
1771                         {
1772                             state[1] = null;
1773                         }
1774                     }
1775                     else
1776                     {
1777                         state[1] = null;
1778                     }
1779                 }
1780                 state[2] = (Integer) listenerCapability;
1781                 return state;
1782             }
1783             else
1784             {
1785                 // If initialStateMarked() == true means two things:
1786                 // 1. PSS is being used
1787                 if ((listenerCapability & LISTENER_TYPE_COMPONENT) != 0)
1788                 {
1789                     return null;
1790                 }
1791                 else if ((listenerCapability & LISTENER_TYPE_RENDERER) != 0)
1792                 {
1793                     return null;
1794                 }
1795                 else
1796                 {
1797                     if ((listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0 ||
1798                             (listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
1799                     {
1800                         Object listenerSaved = ((StateHolder) listener).saveState(context);
1801                         if (listenerSaved == null)
1802                         {
1803                             return null;
1804                         }
1805                         return new Object[]{componentClass,
1806                                             new _AttachedDeltaWrapper(listener.getClass(), listenerSaved)};
1807                     }
1808                     else
1809                     {
1810                         //This is not necessary, because the instance is considered serializable!
1811                         return null;
1812                     }
1813                 }
1814                 /*
1815                 Object listenerSaved = ((StateHolder) listener).saveState(context);
1816                 if (listenerSaved == null)
1817                 {
1818                     return null;
1819                 }
1820                 return new Object[]{componentClass, new _AttachedDeltaWrapper(listener.getClass(), listenerSaved)};
1821                 */
1822             }
1823         }
1824 
1825         public void setTransient(boolean newTransientValue)
1826         {
1827             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 ||
1828                     (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0)
1829             {
1830                 ((StateHolder) listener).setTransient(newTransientValue);
1831             }
1832         }
1833     }
1834 }