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