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