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