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                         // Check if the listener points again to the component, to
880                         // avoid StackoverflowException
881                         boolean shouldProcessEvent = true;
882                         if (listener instanceof EventListenerWrapper && 
883                             ((EventListenerWrapper)listener).listenerCapability == 
884                                 EventListenerWrapper.LISTENER_TYPE_COMPONENT)
885                         {
886                             shouldProcessEvent = false;
887                         }
888                         if (shouldProcessEvent)
889                         {
890                             listener.processEvent(event);
891                         }
892                     }
893                 }
894             }
895         }
896     }
897 
898     public abstract void processValidators(FacesContext context);
899 
900     public abstract void processUpdates(FacesContext context);
901 
902     public abstract java.lang.Object processSaveState(FacesContext context);
903 
904     public void subscribeToEvent(Class<? extends SystemEvent> eventClass,
905                                  ComponentSystemEventListener componentListener)
906     {
907         // The default implementation creates an inner SystemEventListener instance that wraps argument
908         // componentListener as the listener argument.
909         if (eventClass == null)
910         {
911             throw new NullPointerException("eventClass required");
912         }
913         if (componentListener == null)
914         {
915             throw new NullPointerException("componentListener required");
916         }
917 
918         SystemEventListener listener = new EventListenerWrapper(this, componentListener);
919 
920         // Make sure the map exists
921         if (_systemEventListenerClassMap == null)
922         {
923             _systemEventListenerClassMap = new HashMap<Class<? extends SystemEvent>, List<SystemEventListener>>();
924         }
925 
926         List<SystemEventListener> listeners = _systemEventListenerClassMap.get(eventClass);
927         // Make sure the list for class exists
928         if (listeners == null)
929         {
930             // how many listeners per event type can single component have? 
931             // We use 3 here as expected number, but it is a question 
932             listeners = new _DeltaList<SystemEventListener>(new ArrayList<SystemEventListener>(3));
933             _systemEventListenerClassMap.put(eventClass, listeners);
934         }
935 
936         // Deal with contains? Spec is silent
937         listeners.add(listener);
938     }
939 
940     public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass,
941                                      ComponentSystemEventListener componentListener)
942     {
943         /*
944          * When doing the comparison to determine if an existing listener is equal to the argument componentListener
945          * (and thus must be removed), the equals() method on the existing listener must be invoked, passing the
946          * argument componentListener, rather than the other way around.
947          * 
948          * -=Simon Lessard=- What is that supposed to mean? Are we supposed to keep
949          * an internal map of created listener wrappers?
950          * -= Leonardo Uribe=- Yes, it is supposed a wrapper should be used to hold listener references, to prevent
951          * serialize component instances on the state.
952          */
953         if (eventClass == null)
954         {
955             throw new NullPointerException("eventClass required");
956         }
957         if (componentListener == null)
958         {
959             throw new NullPointerException("componentListener required");
960         }
961 
962         if (_systemEventListenerClassMap != null)
963         {
964             List<SystemEventListener> listeners = _systemEventListenerClassMap.get(eventClass);
965 
966             if (listeners != null && !listeners.isEmpty())
967             {
968                 for (Iterator<SystemEventListener> it = listeners.iterator(); it.hasNext(); )
969                 {
970                     ComponentSystemEventListener listener
971                             = ((EventListenerWrapper) it.next()).getComponentSystemEventListener();
972                     if (listener != null && listener.equals(componentListener))
973                     {
974                         it.remove();
975                         break;
976                     }
977                 }
978             }
979         }
980     }
981 
982     /**
983      * The visit tree method, visit tree walks over a subtree and processes
984      * the callback object to perform some operation on the subtree
985      * <p>
986      * there are some details in the implementation which according to the spec have
987      * to be in place:
988      * a) before calling the callback and traversing into the subtree  pushComponentToEL
989      * has to be called
990      * b) after the processing popComponentFromEL has to be performed to remove the component
991      * from the el
992      * </p>
993      * <p>
994      * The tree traversal optimizations are located in the visit context and can be replaced
995      * via the VisitContextFactory in the faces-config factory section
996      * </p>
997      *
998      * @param context the visit context which handles the processing details
999      * @param callback the callback to be performed
1000      * @return false if the processing is not done true if we can shortcut
1001      * the visiting because we are done with everything
1002      *
1003      * @since 2.0
1004      */
1005     public boolean visitTree(VisitContext context, VisitCallback callback)
1006     {
1007         try
1008         {
1009             pushComponentToEL(context.getFacesContext(), this);
1010 
1011             if (!isVisitable(context))
1012             {
1013                 return false;
1014             }
1015 
1016             VisitResult res = context.invokeVisitCallback(this, callback);
1017             switch (res)
1018             {
1019                 //we are done nothing has to be processed anymore
1020                 case COMPLETE:
1021                     return true;
1022 
1023                 case REJECT:
1024                     return false;
1025 
1026                 //accept
1027                 default:
1028                     if (getFacetCount() > 0)
1029                     {
1030                         for (UIComponent facet : getFacets().values())
1031                         {
1032                             if (facet.visitTree(context, callback))
1033                             {
1034                                 return true;
1035                             }
1036                         }
1037                     }
1038                     int childCount = getChildCount();
1039                     if (childCount > 0)
1040                     {
1041                         for (int i = 0; i < childCount; i++)
1042                         {
1043                             UIComponent child = getChildren().get(i);
1044                             if (child.visitTree(context, callback))
1045                             {
1046                                 return true;
1047                             }
1048                         }
1049                     }
1050                     return false;
1051             }
1052         }
1053         finally
1054         {
1055             //all components must call popComponentFromEl after visiting is finished
1056             popComponentFromEL(context.getFacesContext());
1057         }
1058     }
1059 
1060     protected abstract FacesContext getFacesContext();
1061 
1062     protected abstract Renderer getRenderer(FacesContext context);
1063 
1064     /**
1065      * Note that id, clientId properties
1066      * never change its value after the component is populated,
1067      * so we don't need to store it on StateHelper or restore it when
1068      * initialStateMarked == true
1069      * (Note that rendererType is suspicious, in theory this field is
1070      * initialized on constructor, but on 1.1 and 1.2 is saved and restored,
1071      * so to keep backward behavior we put it on StateHelper )
1072      *
1073      * Also, facesListeners can't be wrapped on StateHelper because it
1074      * needs to handle PartialStateHolder instances when it is saved and
1075      * restored and this interface does not implement PartialStateHolder,
1076      * so we can't propagate calls to markInitialState and clearInitialState,
1077      * in other words, the List wrapped by StateHelper does not handle
1078      * PartialStateHolder items.
1079      *
1080      * "bindings" map does not need to deal with PartialStateHolder instances,
1081      *  so we can use StateHelper feature (handle delta for this map or in
1082      *  other words track add/removal from bindings map as delta).
1083      */
1084     enum PropertyKeys
1085     {
1086         rendered,
1087         rendererType,
1088         attributesMap,
1089         bindings,
1090         facesListeners,
1091         passThroughAttributesMap
1092     }
1093 
1094     protected StateHelper getStateHelper()
1095     {
1096         return getStateHelper(true);
1097     }
1098 
1099     /**
1100      * returns a delta state saving enabled state helper
1101      * for the current component
1102      * @param create if true a state helper is created if not already existing
1103      * @return an implementation of the StateHelper interface or null if none exists and create is set to false
1104      */
1105     protected StateHelper getStateHelper(boolean create)
1106     {
1107         if (_stateHelper != null)
1108         {
1109             return _stateHelper;
1110         }
1111         if (create)
1112         {
1113             _stateHelper = new _DeltaStateHelper(this);
1114         }
1115         return _stateHelper;
1116     }
1117 
1118     public final TransientStateHelper getTransientStateHelper()
1119     {
1120         return getTransientStateHelper(true);
1121     }
1122 
1123     public TransientStateHelper getTransientStateHelper(boolean create)
1124     {
1125         if (_stateHelper != null)
1126         {
1127             return _stateHelper;
1128         }
1129         if (create)
1130         {
1131             _stateHelper = new _DeltaStateHelper(this);
1132         }
1133         return _stateHelper;
1134     }
1135 
1136     public void restoreTransientState(FacesContext context, Object state)
1137     {
1138         getTransientStateHelper().restoreTransientState(context, state);
1139     }
1140 
1141     public Object saveTransientState(FacesContext context)
1142     {
1143         return getTransientStateHelper().saveTransientState(context);
1144     }
1145 
1146     @SuppressWarnings("unchecked")
1147     public final void popComponentFromEL(FacesContext context)
1148     {
1149         Map<Object, Object> contextAttributes = context.getAttributes();
1150 
1151         if (_honorCurrentComponentAttributes == null)
1152         {
1153             _honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
1154         }
1155 
1156         if (Boolean.TRUE.equals(_honorCurrentComponentAttributes))
1157         {
1158             // Pop the current UIComponent from the FacesContext attributes map so that the previous 
1159             // UIComponent, if any, becomes the current component.
1160             List<UIComponent> componentStack
1161                     = (List<UIComponent>) contextAttributes.get(UIComponent._COMPONENT_STACK);
1162 
1163             UIComponent oldCurrent = (UIComponent) contextAttributes.get(UIComponent.CURRENT_COMPONENT);
1164 
1165             UIComponent newCurrent = null;
1166             if (componentStack != null && !componentStack.isEmpty())
1167             {
1168                 if (!this.equals(oldCurrent))
1169                 {
1170                     //Check on the componentStack if it can be found
1171                     int componentIndex = componentStack.lastIndexOf(this);
1172                     if (componentIndex >= 0)
1173                     {
1174                         //for (int i = 0; i < (componentIndex + 1); i++)
1175                         for (int i = componentStack.size()-1; i >= componentIndex ; i--)
1176                         {
1177                             newCurrent = componentStack.remove(componentStack.size()-1);
1178                         }
1179                     }
1180                     else
1181                     {
1182                         //Component not found on the stack. Do not pop.
1183                         return;
1184                     }
1185                 }
1186                 else
1187                 {
1188                     newCurrent = componentStack.remove(componentStack.size()-1);
1189                 }
1190             }
1191             else
1192             {
1193                 //Reset the current composite component
1194                 contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, null);
1195             }
1196             oldCurrent = (UIComponent) contextAttributes.put(UIComponent.CURRENT_COMPONENT, newCurrent);
1197 
1198             if (oldCurrent != null && oldCurrent._isCompositeComponent() && newCurrent != null)
1199             {
1200                 // Recalculate the current composite component
1201                 if (newCurrent._isCompositeComponent())
1202                 {
1203                     contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, newCurrent);
1204                 }
1205                 else
1206                 {
1207                     UIComponent previousCompositeComponent = null;
1208                     for (int i = componentStack.size()-1; i >= 0; i--)
1209                     {
1210                         UIComponent component = componentStack.get(i);
1211                         if (component._isCompositeComponent())
1212                         {
1213                             previousCompositeComponent = component;
1214                             break;
1215                         }
1216                     }
1217                     contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, previousCompositeComponent);
1218                 }
1219             }
1220         }
1221         else
1222         {
1223             // Pop the current UIComponent from the FacesContext attributes map so that the previous 
1224             // UIComponent, if any, becomes the current component.
1225             List<UIComponent> componentStack
1226                     = (List<UIComponent>) contextAttributes.get(UIComponent._COMPONENT_STACK);
1227 
1228             UIComponent oldCurrent = null;
1229             if (componentStack != null && !componentStack.isEmpty())
1230             {
1231                 int componentIndex = componentStack.lastIndexOf(this);
1232                 if (componentIndex >= 0)
1233                 {
1234                     for (int i = componentStack.size()-1; i >= componentIndex ; i--)
1235                     {
1236                         oldCurrent = componentStack.remove(componentStack.size()-1);
1237                     }
1238                 }
1239                 else
1240                 {
1241                     return;
1242                 }
1243             }
1244 
1245             if (oldCurrent != null && oldCurrent._isCompositeComponent())
1246             {
1247                 // Recalculate the current composite component
1248                 UIComponent previousCompositeComponent = null;
1249                 for (int i = componentStack.size()-1; i >= 0; i--)
1250                 {
1251                     UIComponent component = componentStack.get(i);
1252                     if (component._isCompositeComponent())
1253                     {
1254                         previousCompositeComponent = component;
1255                         break;
1256                     }
1257                 }
1258                 contextAttributes.put(UIComponent._CURRENT_COMPOSITE_COMPONENT_KEY, previousCompositeComponent);
1259             }
1260         }
1261     }
1262 
1263     @SuppressWarnings("unchecked")
1264     public final void pushComponentToEL(FacesContext context, UIComponent component)
1265     {
1266         if (component == null)
1267         {
1268             component = this;
1269         }
1270 
1271         Map<Object, Object> contextAttributes = context.getAttributes();
1272 
1273         if (_honorCurrentComponentAttributes == null)
1274         {
1275             _honorCurrentComponentAttributes = _getHonorCurrentComponentAttributes(context);
1276         }
1277 
1278         if (Boolean.TRUE.equals(_honorCurrentComponentAttributes))
1279         {
1280             UIComponent currentComponent = (UIComponent) contextAttributes.get(UIComponent.CURRENT_COMPONENT);
1281 
1282             if (currentComponent != null)
1283             {
1284                 List<UIComponent> componentStack
1285                         = (List<UIComponent>) contextAttributes.get(UIComponent._COMPONENT_STACK);
1286                 if (componentStack == null)
1287                 {
1288                     componentStack = new ArrayList<UIComponent>();
1289                     contextAttributes.put(UIComponent._COMPONENT_STACK, componentStack);
1290                 }
1291 
1292                 componentStack.add(currentComponent);
1293             }
1294 
1295             // Push the current UIComponent this to the FacesContext  attribute map using the key CURRENT_COMPONENT 
1296             // saving the previous UIComponent associated with CURRENT_COMPONENT for a subsequent call to 
1297             // popComponentFromEL(javax.faces.context.FacesContext).
1298             contextAttributes.put(UIComponent.CURRENT_COMPONENT, component);
1299 
1300             if (component._isCompositeComponent())
1301             {
1302                 contextAttributes.put(UIComponent.CURRENT_COMPOSITE_COMPONENT, component);
1303             }
1304         }
1305         else
1306         {
1307             List<UIComponent> componentStack
1308                     = (List<UIComponent>) contextAttributes.get(UIComponent._COMPONENT_STACK);
1309             if (componentStack == null)
1310             {
1311                 componentStack = new ArrayList<UIComponent>();
1312                 contextAttributes.put(UIComponent._COMPONENT_STACK, componentStack);
1313             }
1314             componentStack.add(component);
1315             if (component._isCompositeComponent())
1316             {
1317                 contextAttributes.put(UIComponent._CURRENT_COMPOSITE_COMPONENT_KEY, component);
1318             }
1319         }
1320     }
1321 
1322     /**
1323      * @since 1.2
1324      */
1325     public int getFacetCount()
1326     {
1327         // not sure why the RI has this method in both
1328         // UIComponent and UIComponentBase
1329         Map<String, UIComponent> facets = getFacets();
1330         return facets == null ? 0 : facets.size();
1331     }
1332 
1333     private boolean _isCompositeComponent()
1334     {
1335         //moved to the static method
1336         return UIComponent.isCompositeComponent(this);
1337     }
1338     
1339     boolean isCachedFacesContext()
1340     {
1341         return false;
1342     }
1343 
1344     // Dummy method to prevent cast for UIComponentBase when caching
1345     void setCachedFacesContext(FacesContext facesContext)
1346     {
1347     }
1348 
1349     /**
1350      * Gets value of "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES" parameter cached in facesContext.attributes 
1351      * or resolves that param and caches its value in facesContext.attributes.    
1352      *
1353      * @return canonical Boolean value for parameter "javax.faces.HONOR_CURRENT_COMPONENT_ATTRIBUTES"
1354      */
1355     private static Boolean _getHonorCurrentComponentAttributes(FacesContext facesContext)
1356     {
1357         // performance note: we cache value in facesContext.attributes because
1358         // 1) methods pushComponentToEL, popComponentFromEl, getCurrentComponent a getCurrentCompositeComponent
1359         // can use that value
1360         // 2) getExternalContext().getInitParameter has undetermined performance. In typical JSF app, there
1361         // are one or two wrappers around external context; servletContext.getInitParameter has also unknown 
1362         // implementation and performance
1363         Map<Object, Object> attributes = facesContext.getAttributes();
1364         Boolean paramValue = (Boolean) attributes.get(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME);
1365         if (paramValue == null)
1366         {
1367             String param
1368                     = facesContext.getExternalContext().getInitParameter(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME);
1369             paramValue = Boolean.valueOf((param != null && Boolean.valueOf(param).booleanValue()));
1370             attributes.put(HONOR_CURRENT_COMPONENT_ATTRIBUTES_PARAM_NAME, paramValue);
1371         }
1372         return paramValue;
1373     }
1374 
1375     private static class BundleMap implements Map<String, String>
1376     {
1377 
1378         private ResourceBundle _bundle;
1379         private List<String> _values;
1380 
1381         public BundleMap(ResourceBundle bundle)
1382         {
1383             _bundle = bundle;
1384         }
1385 
1386         // Optimized methods
1387         public String get(Object key)
1388         {
1389             try
1390             {
1391                 return (String) _bundle.getObject(key.toString());
1392             }
1393             catch (Exception e)
1394             {
1395                 return "???" + key + "???";
1396             }
1397         }
1398 
1399         public boolean isEmpty()
1400         {
1401             return !_bundle.getKeys().hasMoreElements();
1402         }
1403 
1404         public boolean containsKey(Object key)
1405         {
1406             try
1407             {
1408                 return _bundle.getObject(key.toString()) != null;
1409             }
1410             catch (MissingResourceException e)
1411             {
1412                 return false;
1413             }
1414         }
1415 
1416         // Unoptimized methods
1417         public Collection<String> values()
1418         {
1419             if (_values == null)
1420             {
1421                 _values = new ArrayList<String>();
1422                 for (Enumeration<String> enumer = _bundle.getKeys(); enumer.hasMoreElements(); )
1423                 {
1424                     String v = _bundle.getString(enumer.nextElement());
1425                     _values.add(v);
1426                 }
1427             }
1428             return _values;
1429         }
1430 
1431         public int size()
1432         {
1433             return values().size();
1434         }
1435 
1436         public boolean containsValue(Object value)
1437         {
1438             return values().contains(value);
1439         }
1440 
1441         public Set<Map.Entry<String, String>> entrySet()
1442         {
1443             Set<Entry<String, String>> set = new HashSet<Entry<String, String>>();
1444             for (Enumeration<String> enumer = _bundle.getKeys(); enumer.hasMoreElements(); )
1445             {
1446                 final String k = enumer.nextElement();
1447                 set.add(new Map.Entry<String, String>()
1448                 {
1449 
1450                     public String getKey()
1451                     {
1452                         return k;
1453                     }
1454 
1455                     public String getValue()
1456                     {
1457                         return (String) _bundle.getObject(k);
1458                     }
1459 
1460                     public String setValue(String value)
1461                     {
1462                         throw new UnsupportedOperationException();
1463                     }
1464                 });
1465             }
1466 
1467             return set;
1468         }
1469 
1470         public Set<String> keySet()
1471         {
1472             Set<String> set = new HashSet<String>();
1473             for (Enumeration<String> enumer = _bundle.getKeys(); enumer.hasMoreElements(); )
1474             {
1475                 set.add(enumer.nextElement());
1476             }
1477             return set;
1478         }
1479 
1480         // Unsupported methods
1481         public String remove(Object key)
1482         {
1483             throw new UnsupportedOperationException();
1484         }
1485 
1486         public void putAll(Map<? extends String, ? extends String> t)
1487         {
1488             throw new UnsupportedOperationException();
1489         }
1490 
1491         public String put(String key, String value)
1492         {
1493             throw new UnsupportedOperationException();
1494         }
1495 
1496         public void clear()
1497         {
1498             throw new UnsupportedOperationException();
1499         }
1500     }
1501 
1502     static class EventListenerWrapper implements SystemEventListener, PartialStateHolder
1503     {
1504 
1505         private Class<?> componentClass;
1506         private ComponentSystemEventListener listener;
1507 
1508         private boolean _initialStateMarked;
1509 
1510         private int listenerCapability;
1511         private transient UIComponent _component;
1512 
1513         private static final int LISTENER_SAVE_STATE_HOLDER = 1;
1514         private static final int LISTENER_SAVE_PARTIAL_STATE_HOLDER = 2;
1515         private static final int LISTENER_TYPE_COMPONENT = 4;
1516         private static final int LISTENER_TYPE_RENDERER = 8;
1517         private static final int LISTENER_TYPE_OTHER = 16;
1518 
1519         public EventListenerWrapper()
1520         {
1521             //need a no-arg constructor for state saving purposes
1522             super();
1523         }
1524 
1525         /**
1526          * Note we have two cases:
1527          *
1528          * 1. listener is an instance of UIComponent. In this case we cannot save and restore
1529          *    it because we need to point to the real component, but we can assume the instance
1530          *    is the same because UIComponent.subscribeToEvent says so. Also take into account
1531          *    this case is the reason why we need a wrapper for UIComponent.subscribeToEvent
1532          * 2. listener is an instance of Renderer. In this case we can assume the same renderer
1533          *    used by the source component is the one used by the listener (ListenerFor). 
1534          * 3. listener is an instance of ComponentSystemEventListener but not from UIComponent.
1535          *    In this case, the instance could implement StateHolder, PartialStateHolder or do
1536          *    implement anything, so we have to deal with that case as usual.
1537          *
1538          * @param component
1539          * @param listener
1540          */
1541         public EventListenerWrapper(UIComponent component, ComponentSystemEventListener listener)
1542         {
1543             assert component != null;
1544             assert listener != null;
1545 
1546             this.componentClass = component.getClass();
1547             this.listener = listener;
1548             this._component = component;
1549             initListenerCapability();
1550         }
1551 
1552         private void initListenerCapability()
1553         {
1554             this.listenerCapability = 0;
1555             if (this.listener instanceof UIComponent)
1556             {
1557                 this.listenerCapability = LISTENER_TYPE_COMPONENT;
1558             }
1559             else if (this.listener instanceof Renderer)
1560             {
1561                 this.listenerCapability = LISTENER_TYPE_RENDERER;
1562             }
1563             else
1564             {
1565                 if (this.listener instanceof PartialStateHolder)
1566                 {
1567                     this.listenerCapability = LISTENER_TYPE_OTHER | LISTENER_SAVE_PARTIAL_STATE_HOLDER;
1568                 }
1569                 else if (this.listener instanceof StateHolder)
1570                 {
1571                     this.listenerCapability = LISTENER_TYPE_OTHER | LISTENER_SAVE_STATE_HOLDER;
1572                 }
1573                 else
1574                 {
1575                     this.listenerCapability = LISTENER_TYPE_OTHER;
1576                 }
1577             }
1578         }
1579 
1580         @Override
1581         public boolean equals(Object o)
1582         {
1583             if (o == this)
1584             {
1585                 return true;
1586             }
1587             else if (o instanceof EventListenerWrapper)
1588             {
1589                 EventListenerWrapper other = (EventListenerWrapper) o;
1590                 return componentClass.equals(other.componentClass) && listener.equals(other.listener);
1591             }
1592             else
1593             {
1594                 return false;
1595             }
1596         }
1597 
1598         @Override
1599         public int hashCode()
1600         {
1601             return componentClass.hashCode() + listener.hashCode();
1602         }
1603 
1604         public boolean isListenerForSource(Object source)
1605         {
1606             // and its implementation of SystemEventListener.isListenerForSource(java.lang.Object) must return true
1607             // if the instance class of this UIComponent is assignable from the argument to isListenerForSource.
1608 
1609             return source.getClass().isAssignableFrom(componentClass);
1610         }
1611 
1612         public ComponentSystemEventListener getComponentSystemEventListener()
1613         {
1614             return listener;
1615         }
1616 
1617         public void processEvent(SystemEvent event)
1618         {
1619             // This inner class must call through to the argument componentListener in its implementation of
1620             // SystemEventListener.processEvent(javax.faces.event.SystemEvent)
1621 
1622             assert event instanceof ComponentSystemEvent;
1623 
1624             listener.processEvent((ComponentSystemEvent) event);
1625         }
1626 
1627         public void clearInitialState()
1628         {
1629             //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
1630             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
1631             {
1632                 ((PartialStateHolder) listener).clearInitialState();
1633             }
1634             _initialStateMarked = false;
1635         }
1636 
1637         public boolean initialStateMarked()
1638         {
1639             //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
1640             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
1641             {
1642                 return ((PartialStateHolder) listener).initialStateMarked();
1643             }
1644             //return false;
1645             return _initialStateMarked;
1646         }
1647 
1648         public void markInitialState()
1649         {
1650             //if (!(listener instanceof UIComponent) && listener instanceof PartialStateHolder)
1651             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
1652             {
1653                 ((PartialStateHolder) listener).markInitialState();
1654             }
1655             _initialStateMarked = true;
1656         }
1657 
1658         public boolean isTransient()
1659         {
1660             //if ( listener instanceof StateHolder)
1661             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 ||
1662                     (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0)
1663             {
1664                 return ((StateHolder) listener).isTransient();
1665             }
1666             return false;
1667         }
1668 
1669         public void restoreState(FacesContext context, Object state)
1670         {
1671             if (state == null)
1672             {
1673                 return;
1674             }
1675             Object[] values = (Object[]) state;
1676             componentClass = (Class) values[0];
1677             if (values[1] instanceof _AttachedDeltaWrapper)
1678             {
1679                 ((StateHolder) listener).restoreState(context,
1680                         ((_AttachedDeltaWrapper) values[1]).getWrappedStateObject());
1681             }
1682             else
1683             {
1684                 //Full restore
1685                 listenerCapability = (Integer) values[2];
1686 
1687                 _component = UIComponent.getCurrentComponent(context);
1688                 if ((listenerCapability & LISTENER_TYPE_COMPONENT) != 0)
1689                 {
1690                     listener = _component;
1691                 }
1692                 else if ((listenerCapability & LISTENER_TYPE_RENDERER) != 0)
1693                 {
1694                     //listener = (ComponentSystemEventListener)
1695                     //        UIComponent.getCurrentComponent(context).getRenderer(context);
1696                     Renderer renderer = _component.getRenderer(context);
1697                     Integer i = (Integer) values[1];
1698                     if (i != null && i >= 0)
1699                     {
1700                         while (i > 0)
1701                         {
1702                             renderer = ((RendererWrapper) renderer).getWrapped();
1703                             i--;
1704                         }
1705                     }
1706                     listener = (ComponentSystemEventListener) renderer;
1707                 }
1708                 else
1709                 {
1710                     listener = (ComponentSystemEventListener)
1711                             UIComponentBase.restoreAttachedState(context, values[1]);
1712                 }
1713                 /*
1714                 listener = values[1] == null ? 
1715                         UIComponent.getCurrentComponent(context) : 
1716                             (ComponentSystemEventListener) UIComponentBase.restoreAttachedState(context, values[1]);
1717                             */
1718             }
1719         }
1720 
1721         public Object saveState(FacesContext context)
1722         {
1723             if (!initialStateMarked())
1724             {
1725                 /*
1726                 Object[] state = new Object[2];
1727                 state[0] = componentClass;
1728                 if (!(listener instanceof UIComponent))
1729                 {
1730                     state[1] = UIComponentBase.saveAttachedState(context, listener);
1731                 }
1732                 return state;
1733                 */
1734                 Object[] state = new Object[3];
1735                 state[0] = componentClass;
1736                 //If this is not a component or a renderer, save it calling UIComponent.saveAttachedState
1737                 if (!((listenerCapability & LISTENER_TYPE_COMPONENT) != 0 ||
1738                         (listenerCapability & LISTENER_TYPE_RENDERER) != 0))
1739                 {
1740                     state[1] = UIComponentBase.saveAttachedState(context, listener);
1741                 }
1742                 else
1743                 {
1744                     if ( (listenerCapability & LISTENER_TYPE_RENDERER) != 0)
1745                     {
1746                         UIComponent componentRef = _component != null ? _component : getCurrentComponent(context);
1747                         Renderer renderer = componentRef.getRenderer(context);
1748                         int i = 0;
1749                         while (renderer != null && !renderer.getClass().equals(listener.getClass()))
1750                         {
1751                             if (renderer instanceof RendererWrapper)
1752                             {
1753                                 renderer = ((RendererWrapper) renderer).getWrapped();
1754                                 i++;
1755                             }
1756                             else
1757                             {
1758                                 renderer = null;
1759                                 i = -1;
1760                             }
1761                         }
1762                         if (i != -1)
1763                         {
1764                             // Store the number so we can get the right wrapper to invoke the method.
1765                             state[1] = i;
1766                         }
1767                         else
1768                         {
1769                             state[1] = null;
1770                         }
1771                     }
1772                     else
1773                     {
1774                         state[1] = null;
1775                     }
1776                 }
1777                 state[2] = (Integer) listenerCapability;
1778                 return state;
1779             }
1780             else
1781             {
1782                 // If initialStateMarked() == true means two things:
1783                 // 1. PSS is being used
1784                 if ((listenerCapability & LISTENER_TYPE_COMPONENT) != 0)
1785                 {
1786                     return null;
1787                 }
1788                 else if ((listenerCapability & LISTENER_TYPE_RENDERER) != 0)
1789                 {
1790                     return null;
1791                 }
1792                 else
1793                 {
1794                     if ((listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0 ||
1795                             (listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0)
1796                     {
1797                         Object listenerSaved = ((StateHolder) listener).saveState(context);
1798                         if (listenerSaved == null)
1799                         {
1800                             return null;
1801                         }
1802                         return new Object[]{componentClass,
1803                                             new _AttachedDeltaWrapper(listener.getClass(), listenerSaved)};
1804                     }
1805                     else
1806                     {
1807                         //This is not necessary, because the instance is considered serializable!
1808                         return null;
1809                     }
1810                 }
1811                 /*
1812                 Object listenerSaved = ((StateHolder) listener).saveState(context);
1813                 if (listenerSaved == null)
1814                 {
1815                     return null;
1816                 }
1817                 return new Object[]{componentClass, new _AttachedDeltaWrapper(listener.getClass(), listenerSaved)};
1818                 */
1819             }
1820         }
1821 
1822         public void setTransient(boolean newTransientValue)
1823         {
1824             if ((listenerCapability & LISTENER_SAVE_PARTIAL_STATE_HOLDER) != 0 ||
1825                     (listenerCapability & LISTENER_SAVE_STATE_HOLDER) != 0)
1826             {
1827                 ((StateHolder) listener).setTransient(newTransientValue);
1828             }
1829         }
1830     }
1831 }