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