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.io.Serializable;
23  import java.lang.reflect.Array;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  
30  import javax.faces.FactoryFinder;
31  import javax.faces.FacesException;
32  import javax.faces.context.FacesContext;
33  import javax.faces.el.ValueBinding;
34  import javax.faces.event.AbortProcessingException;
35  import javax.faces.event.FacesEvent;
36  import javax.faces.event.FacesListener;
37  import javax.faces.render.RenderKit;
38  import javax.faces.render.RenderKitFactory;
39  import javax.faces.render.Renderer;
40  
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  
44  /**
45   * Standard implementation of the UIComponent base class; all standard JSF
46   * components extend this class.
47   * <p>
48   * <i>Disclaimer</i>: The official definition for the behaviour of
49   * this class is the JSF 1.1 specification but for legal reasons the
50   * specification cannot be replicated here. Any javadoc here therefore
51   * describes the current implementation rather than the spec, though
52   * this class has been verified as correctly implementing the spec.
53   * <p>
54   * See Javadoc of <a href="http://java.sun.com/j2ee/javaserverfaces/1.1_01/docs/api/index.html">JSF Specification</a> for more.
55   * 
56   * @JSFComponent
57   *   type = "javax.faces.ComponentBase"
58   *   family = "javax.faces.ComponentBase"
59   *   desc = "base component from which all components should inherit"
60   *   tagClass = "javax.faces.webapp.UIComponentTag"
61   *   
62   * @JSFJspProperty
63   *   name = "binding" 
64   *   returnType = "java.lang.String"
65   *   longDesc = "Identifies a backing bean property (of type UIComponent or appropriate subclass) to bind to this component instance. This value must be an EL expression."
66   *   desc="backing bean property to bind to this component instance"
67   *   
68   * @author Manfred Geiler (latest modification by $Author: lu4242 $)
69   * @version $Revision: 949070 $ $Date: 2010-05-27 21:22:03 -0500 (Thu, 27 May 2010) $
70   */
71  public abstract class UIComponentBase
72          extends UIComponent
73  {
74      private static Log log = LogFactory.getLog(UIComponentBase.class);
75      
76      private static final Iterator _EMPTY_UICOMPONENT_ITERATOR = 
77          new _EmptyIterator();    
78  
79      private _ComponentAttributesMap _attributesMap = null;
80      private Map _valueBindingMap = null;
81      private List _childrenList = null;
82      private Map _facetMap = null;
83      private List _facesListeners = null;
84      private String _clientId = null;
85      private String _id = null;
86      private UIComponent _parent = null;
87      private boolean _transient = false;
88  
89      private transient FacesContext _facesContext;
90  
91      public UIComponentBase()
92      {
93      }
94  
95      /**
96       * Get a map through which all the UIComponent's properties, value-bindings
97       * and non-property attributes can be read and written.
98       * <p>
99       * When writing to the returned map:
100      * <ul>
101      * <li>If this component has an explicit property for the specified key
102      *  then the setter method is called. An IllegalArgumentException is
103      *  thrown if the property is read-only. If the property is readable
104      *  then the old value is returned, otherwise null is returned.
105      * <li>Otherwise the key/value pair is stored in a map associated with
106      * the component.
107      * </ul>
108      * Note that value-bindings are <i>not</i> written by put calls to this map.
109      * Writing to the attributes map using a key for which a value-binding 
110      * exists will just store the value in the attributes map rather than
111      * evaluating the binding, effectively "hiding" the value-binding from
112      * later attributes.get calls. Setter methods on components commonly do
113      * <i>not</i> evaluate a binding of the same name; they just store the
114      * provided value directly on the component.
115      * <p>
116      * When reading from the returned map:
117      * <ul>
118      * <li>If this component has an explicit property for the specified key
119      *  then the getter method is called. If the property exists, but is
120      *  read-only (ie only a setter method is defined) then an
121      *  IllegalArgumentException is thrown.
122      * <li>If the attribute map associated with the component has an entry
123      *  with the specified key, then that is returned.
124      * <li>If this component has a value-binding for the specified key, then
125      * the value-binding is evaluated to fetch the value.
126      * <li>Otherwise, null is returned.
127      * </ul>
128      * Note that components commonly define getter methods such that they
129      * evaluate a value-binding of the same name if there isn't yet a
130      * local property.
131      * <p>
132      * Assigning values to the map which are not explicit properties on
133      * the underlying component can be used to "tunnel" attributes from
134      * the JSP tag (or view-specific equivalent) to the associated renderer
135      * without modifying the component itself.
136      * <p>
137      * Any value-bindings and non-property attributes stored in this map
138      * are automatically serialized along with the component when the view
139      * is serialized.
140      */
141     public Map getAttributes()
142     {
143         if (_attributesMap == null)
144         {
145             _attributesMap = new _ComponentAttributesMap(this);
146         }
147         return _attributesMap;
148     }
149 
150     /**
151      * Get the named value-binding associated with this component.
152      * <p>
153      * Value-bindings are stored in a map associated with the component,
154      * though there is commonly a property (setter/getter methods) 
155      * of the same name defined on the component itself which
156      * evaluates the value-binding when called.
157      */
158     public ValueBinding getValueBinding(String name)
159     {
160         if (name == null) throw new NullPointerException("name");
161         if (_valueBindingMap == null)
162         {
163             return null;
164         }
165         else
166         {
167             return (ValueBinding)_valueBindingMap.get(name);
168         }
169     }
170 
171     /**
172      * Put the provided value-binding into a map of value-bindings
173      * associated with this component.
174      */
175     public void setValueBinding(String name,
176                                 ValueBinding binding)
177     {
178         if (name == null) throw new NullPointerException("name");
179         if (_valueBindingMap == null)
180         {
181             _valueBindingMap = new HashMap();
182         }
183         _valueBindingMap.put(name, binding);
184     }
185 
186     /**
187      * Get a string which can be output to the response which uniquely
188      * identifies this UIComponent within the current view.
189      * <p>
190      * The component should have an id attribute already assigned to it;
191      * however if the id property is currently null then a unique id
192      * is generated and set for this component. This only happens when
193      * components are programmatically created without ids, as components
194      * created by a ViewHandler should be assigned ids when they are created.
195      * <p>
196      * If this component is a descendant of a NamingContainer then the
197      * client id is of form "{namingContainerId}:{componentId}". Note that
198      * the naming container's id may itself be of compound form if it has
199      * an ancestor naming container. Note also that this only applies to
200      * naming containers; other UIComponent types in the component's
201      * ancestry do not affect the clientId.
202      * <p>
203      * Finally the renderer associated with this component is asked to
204      * convert the id into a suitable form. This allows escaping of any
205      * characters in the clientId which are significant for the markup
206      * language generated by that renderer.
207      */
208     public String getClientId(FacesContext context)
209     {
210         if (context == null) throw new NullPointerException("context");
211 
212         if (_clientId != null) return _clientId;
213 
214         boolean idWasNull = false;
215         String id = getId();
216         if (id == null)
217         {
218             //Although this is an error prone side effect, we automatically create a new id
219             //just to be compatible to the RI
220             UIViewRoot viewRoot = context.getViewRoot();
221             if (viewRoot != null)
222             {
223                 id = viewRoot.createUniqueId();
224             }
225             else
226             {
227                 context.getExternalContext().log("ERROR: Cannot automatically create an id for component of type " + getClass().getName() + " because there is no viewRoot in the current facesContext!");
228                 id = "ERROR";
229             }
230             setId(id);
231             //We remember that the id was null and log a warning down below
232             idWasNull = true;
233         }
234 
235         UIComponent namingContainer = findParentNamingContainer(this, false);
236         if (namingContainer != null)
237         {
238             _clientId = namingContainer.getClientId(context) + NamingContainer.SEPARATOR_CHAR + id;
239         }
240         else
241         {
242             _clientId = id;
243         }
244 
245         Renderer renderer = getRenderer(context);
246         if (renderer != null)
247         {
248             _clientId = renderer.convertClientId(context, _clientId);
249         }
250 
251         if (idWasNull)
252         {
253             context.getExternalContext().log("WARNING: Component " + _clientId + " just got an automatic id, because there was no id assigned yet. " +
254                                              "If this component was created dynamically (i.e. not by a JSP tag) you should assign it an " +
255                                              "explicit static id or assign it the id you get from the createUniqueId from the current UIViewRoot " +
256                                              "component right after creation!");
257         }
258 
259         return _clientId;
260     }
261 
262     /**
263      * An identifier for this particular component instance within
264      * a component view.
265      * <p>
266      * The id must be unique within the scope of the tag's enclosing 
267      * NamingContainer (eg h:form or f:subview). The id is
268      * not necessarily unique across all components in the current view
269      * </p>
270      * <p>
271      * This value must be a static value, ie not change over the lifetime
272      * of a component. It cannot be defined via an EL expression; only
273      * a string is permitted.
274      * </p>
275      * 
276      * @JSFProperty
277      */
278     public String getId()
279     {
280         return _id;
281     }
282 
283     /**
284      * Set an identifier for this component which is unique within the
285      * scope of the nearest ancestor NamingContainer component. The id is
286      * not necessarily unique across all components in the current view.
287      * <p>
288      * The id must start with an underscore if it is generated by the JSF
289      * framework, and must <i>not</i> start with an underscore if it has
290      * been specified by the user (eg in a JSP tag).
291      * <p>
292      * The first character of the id must be an underscore or letter.
293      * Following characters may be letters, digits, underscores or dashes.
294      * <p>
295      * Null is allowed as a parameter, and will reset the id to null.
296      * <p>
297      * The clientId of this component is reset by this method; see
298      * getClientId for more info.
299      *  
300      * @throws IllegalArgumentException if the id is not valid.
301      */
302     public void setId(String id)
303     {
304         isIdValid(id);
305         _id = id;
306         _clientId = null;
307 
308         UIComponent parent = getParent();
309 
310         if(parent != null) {
311             List li = getParent().getChildren();
312 
313             if(li instanceof _ComponentChildrenList) {
314                 ((_ComponentChildrenList) li).updateId(_id,this);
315             }
316         }
317     }
318 
319     public UIComponent getParent()
320     {
321         return _parent;
322     }
323 
324     public void setParent(UIComponent parent)
325     {
326         _parent = parent;
327     }
328 
329     /**
330      * Indicates whether this component or its renderer manages the
331      * invocation of the rendering methods of its child components.
332      * When this is true:
333      * <ul>
334      * <li>This component's encodeBegin method will only be called
335      * after all the child components have been created and added
336      * to this component.
337      * <li>This component's encodeChildren method will be called
338      * after its encodeBegin method. Components for which this
339      * method returns false do not get this method invoked at all.
340      * <li>No rendering methods will be called automatically on
341      * child components; this component is required to invoke the
342      * encodeBegin/encodeEnd/etc on them itself.
343      * </ul>
344      */
345     public boolean getRendersChildren()
346     {
347         Renderer renderer = getRenderer(getFacesContext());
348         if (renderer != null)
349         {
350             return renderer.getRendersChildren();
351         }
352         else
353         {
354             return false;
355         }
356     }
357 
358     /**
359      * Return a list of the UIComponent objects which are direct children
360      * of this component.
361      * <p>
362      * The list object returned has some non-standard behaviour:
363      * <ul>
364      * <li>The list is type-checked; only UIComponent objects can be added.
365      * <li>If a component is added to the list with an id which is the same
366      * as some other component in the list then an exception is thrown. However
367      * multiple components with a null id may be added.
368      * <li>The component's parent property is set to this component. If the
369      * component already had a parent, then the component is first removed
370      * from its original parent's child list.
371      * </ul>
372      */
373     public List getChildren()
374     {
375         if (_childrenList == null)
376         {
377             _childrenList = new _ComponentChildrenList(this);
378         }
379         return _childrenList;
380     }
381 
382     /**
383      * Return the number of direct child components this component has.
384      * <p>
385      * Identical to getChildren().size() except that when this component
386      * has no children this method will not force an empty list to be
387      * created.
388      */
389     public int getChildCount()
390     {
391         return _childrenList == null ? 0 : _childrenList.size();
392     }
393 
394     /**
395      * Standard method for finding other components by id, inherited by
396      * most UIComponent objects.
397      * <p>
398      * The lookup is performed in a manner similar to finding a file
399      * in a filesystem; there is a "base" at which to start, and the
400      * id can be for something in the "local directory", or can include
401      * a relative path. Here, NamingContainer components fill the role
402      * of directories, and ":" is the "path separator". Note, however,
403      * that although components have a strict parent/child hierarchy,
404      * component ids are only prefixed ("namespaced") with the id of
405      * their parent when the parent is a NamingContainer.
406      * <p>
407      * The base node at which the search starts is determined as
408      * follows:
409      * <ul>
410      * <li>When expr starts with ':', the search starts with the root
411      * component of the tree that this component is in (ie the ancestor
412      * whose parent is null).
413      * <li>Otherwise, if this component is a NamingContainer then the search
414      * starts with this component.
415      * <li>Otherwise, the search starts from the nearest ancestor 
416      * NamingContainer (or the root component if there is no NamingContainer
417      * ancestor).
418      * </ul>
419      * 
420      * @param expr is of form "id1:id2:id3".
421      * @return UIComponent or null if no component with the specified id is
422      * found.
423      */
424 
425     public UIComponent findComponent(String expr)
426     {
427         if (expr == null) throw new NullPointerException("expr");
428         if (expr.length() == 0) throw new IllegalArgumentException("empty expr"); //TODO: not specified!
429 
430         UIComponent findBase;
431         if (expr.charAt(0) == NamingContainer.SEPARATOR_CHAR)
432         {
433             findBase = getRootComponent(this);
434             expr = expr.substring(1);
435         }
436         else
437         {
438             if (this instanceof NamingContainer)
439             {
440                 findBase = this;
441             }
442             else
443             {
444                 findBase = findParentNamingContainer(this, true /* root if not found */);
445             }
446         }
447 
448         int separator = expr.indexOf(NamingContainer.SEPARATOR_CHAR);
449         if (separator == -1)
450         {
451             return findComponent(findBase, expr);
452         }
453         else
454         {
455             String id = expr.substring(0, separator);
456             findBase = findComponent(findBase, id);
457             if (findBase == null)
458             {
459                 return null;
460             }
461             else
462             {
463                 if (!(findBase instanceof NamingContainer))
464                     throw new IllegalArgumentException("Intermediate identifier " + id + " in search expression " +
465                         expr + " identifies a UIComponent that is not a NamingContainer");
466                 return findBase.findComponent(expr.substring(separator + 1));
467             }
468         }
469     }
470 
471     static UIComponent findParentNamingContainer(UIComponent component,
472                                                  boolean returnRootIfNotFound)
473     {
474         UIComponent parent = component.getParent();
475         if (returnRootIfNotFound && parent == null)
476         {
477             return component;
478         }
479         while (parent != null)
480         {
481             if (parent instanceof NamingContainer) return parent;
482             if (returnRootIfNotFound)
483             {
484                 UIComponent nextParent = parent.getParent();
485                 if (nextParent == null)
486                 {
487                     return parent;  //Root
488                 }
489                 parent = nextParent;
490             }
491             else
492             {
493                 parent = parent.getParent();
494             }
495         }
496         return null;
497     }
498 
499     static UIComponent getRootComponent(UIComponent component)
500     {
501         UIComponent parent;
502         for(;;)
503         {
504             parent = component.getParent();
505             if (parent == null) return component;
506             component = parent;
507         }
508     }
509 
510     /**
511      * Find the component with the specified id starting from the specified
512      * component.
513      * <p>
514      * Param id must not contain any NamingContainer.SEPARATOR_CHAR characters
515      * (ie ":"). This method explicitly does <i>not</i> search into any
516      * child naming container components; this is expected to be handled
517      * by the caller of this method.
518      * <p>
519      * For an implementation of findComponent which does descend into
520      * child naming components, see org.apache.myfaces.custom.util.ComponentUtils.
521      *
522      * @return findBase, a descendant of findBase, or null.
523      */
524     private UIComponent findComponent(UIComponent findBase, String id)
525     {
526         if (idsAreEqual(id, findBase)){
527             return findBase;
528         }
529 
530         /*UIComponent comp = findComponentCached(id, findBase);
531 
532         if(comp != null)
533             return comp;*/     
534 
535         return findComponentNormal(id, findBase);
536     }
537 
538     private UIComponent findComponentCached(String id, UIComponent findBase) {
539 
540         if(!(findBase.getChildren() instanceof _ComponentChildrenList)) {
541             return null;
542         }
543 
544         _ComponentChildrenList li = (_ComponentChildrenList) findBase.getChildren();
545 
546         UIComponent comp = li.get(id);
547 
548         if(comp != null)
549             return comp;
550 
551         for (Iterator it = findBase.getFacetsAndChildren(); it.hasNext(); )
552         {
553             UIComponent childOrFacet = (UIComponent)it.next();
554             UIComponent find = findComponentCached(id,childOrFacet);
555             if (find != null) return find;
556         }
557 
558         return null;
559     }
560 
561     private UIComponent findComponentNormal(String id, UIComponent findBase) {
562         if (idsAreEqual(id, findBase))
563         {
564             return findBase;
565         }
566 
567         for (Iterator it = findBase.getFacetsAndChildren(); it.hasNext(); )
568         {
569             UIComponent childOrFacet = (UIComponent)it.next();
570             // If a descendant NamingContainer is found, 
571             // child components including facets are not searched,
572             // so just check if this component is and continue
573             // if not.
574             if (childOrFacet instanceof NamingContainer){
575                 if (idsAreEqual(id, childOrFacet))
576                 {
577                     return childOrFacet;
578                 }
579             }else{
580                 UIComponent find = findComponentNormal(id, childOrFacet);
581                 if (find != null) return find;
582             }
583         }
584 
585         return null;
586     }
587 
588     /*
589      * Return true if the specified component matches the provided id.
590      * This needs some quirks to handle components whose id value gets
591      * dynamically "tweaked", eg a UIData component whose id gets
592      * the current row index appended to it.
593      */
594     private boolean idsAreEqual(String id, UIComponent cmp)
595     {
596         if(id.equals(cmp.getId()))
597             return true;
598 
599         if(cmp instanceof UIData)
600         {
601             UIData uiData = ((UIData) cmp);
602 
603             if(uiData.getRowIndex()==-1)
604             {
605                 return dynamicIdIsEqual(id,cmp.getId());
606             }
607             else
608             {
609                 return id.equals(cmp.getId()+NamingContainer.SEPARATOR_CHAR+uiData.getRowIndex());
610             }
611         }
612 
613         return false;
614     }
615 
616     private boolean dynamicIdIsEqual(String dynamicId, String id)
617     {
618         return dynamicId.matches(id+":[0-9]*");
619     }
620 
621 
622     public Map getFacets()
623     {
624         if (_facetMap == null)
625         {
626             _facetMap = new _ComponentFacetMap(this);
627         }
628         return _facetMap;
629     }
630 
631     public UIComponent getFacet(String name)
632     {
633         return _facetMap == null ? null : (UIComponent)_facetMap.get(name);
634     }
635 
636     public Iterator getFacetsAndChildren()
637     {
638         if (_facetMap == null)
639         {
640             if (_childrenList == null)
641                 return _EMPTY_UICOMPONENT_ITERATOR;
642 
643             if (_childrenList.size() == 0)
644                 return _EMPTY_UICOMPONENT_ITERATOR;
645             
646             return _childrenList.iterator();
647         }
648         else
649         {
650             if (_facetMap.size() == 0)
651             {
652                 if (_childrenList == null)
653                     return _EMPTY_UICOMPONENT_ITERATOR;
654               
655                 if (_childrenList.size() == 0)
656                     return _EMPTY_UICOMPONENT_ITERATOR;
657             
658                 return _childrenList.iterator();  
659             }
660             else
661             {
662                 if (_childrenList == null)
663                     return _facetMap.values().iterator();
664               
665                 if (_childrenList.size() == 0)
666                     return _facetMap.values().iterator();
667               
668                 return new _FacetsAndChildrenIterator(_facetMap, _childrenList);                            
669             }
670         }
671     }
672 
673     /**
674      * Invoke any listeners attached to this object which are listening
675      * for an event whose type matches the specified event's runtime
676      * type.
677      * <p>
678      * This method does not propagate the event up to parent components,
679      * ie listeners attached to parent components don't automatically
680      * get called.
681      * <p>
682      * If any of the listeners throws AbortProcessingException then
683      * that exception will prevent any further listener callbacks
684      * from occurring, and the exception propagates out of this
685      * method without alteration.
686      * <p>
687      * ActionEvent events are typically queued by the renderer associated
688      * with this component in its decode method; ValueChangeEvent events by
689      * the component's validate method. In either case the event's source
690      * property references a component. At some later time the UIViewRoot
691      * component iterates over its queued events and invokes the broadcast
692      * method on each event's source object.
693      * 
694      * @param event must not be null.
695      */
696     public void broadcast(FacesEvent event)
697             throws AbortProcessingException
698     {
699         if (event == null) throw new NullPointerException("event");
700         try {
701             if (_facesListeners == null) return;
702             for (Iterator it = _facesListeners.iterator(); it.hasNext(); )
703             {
704                 FacesListener facesListener = (FacesListener)it.next();
705                 if (event.isAppropriateListener(facesListener))
706                 {
707                     event.processListener(facesListener);
708                 }
709             }
710         } catch (Exception e) {
711             if (e instanceof AbortProcessingException) {
712                 throw (AbortProcessingException) e;
713             }
714             throw new FacesException("Exception while calling broadcast on : "+getPathToComponent(this), e);
715         }
716     }
717 
718     /**
719      * Check the submitted form parameters for data associated with this
720      * component. This default implementation delegates to this component's
721      * renderer if there is one, and otherwise ignores the call.
722      */
723     public void decode(FacesContext context)
724     {
725         if (context == null) throw new NullPointerException("context");
726         try {
727             Renderer renderer = getRenderer(context);
728             if (renderer != null)
729             {
730                 renderer.decode(context, this);
731             }
732         } catch (Exception e) {
733             if (e instanceof AbortProcessingException)
734             throw new FacesException("Exception while calling decode on : "+getPathToComponent(this), e);
735         }
736     }
737 
738     public void encodeBegin(FacesContext context)
739             throws IOException
740     {
741         if (context == null) throw new NullPointerException("context");
742         try {
743             setCachedFacesContext(context);
744             
745             if (!isRendered()) return;
746             Renderer renderer = getRenderer(context);
747             if (renderer != null)
748             {
749                 renderer.encodeBegin(context, this);
750             }
751         } catch (Exception e) {
752             throw new FacesException("Exception while calling encodeBegin on : "+getPathToComponent(this), e);
753         }
754         finally
755         {
756             setCachedFacesContext(null);
757         }
758     }
759 
760     public void encodeChildren(FacesContext context)
761             throws IOException
762     {
763         if (context == null) throw new NullPointerException("context");
764         
765         boolean isCachedFacesContext = isCachedFacesContext();
766         try
767         {
768             if (!isCachedFacesContext)
769             {
770                 setCachedFacesContext(context);
771             }
772             if (!isRendered()) return;
773             Renderer renderer = getRenderer(context);
774             if (renderer != null)
775             {
776                 renderer.encodeChildren(context, this);
777             }
778         }
779         finally
780         {
781             if (!isCachedFacesContext)
782             {
783                 setCachedFacesContext(null);
784             }
785         }
786     }
787 
788     public void encodeEnd(FacesContext context)
789             throws IOException
790     {
791         if (context == null) throw new NullPointerException("context");
792         try {
793             setCachedFacesContext(context);
794             if (!isRendered()) return;
795 
796             Renderer renderer = getRenderer(context);
797             if (renderer != null)
798             {
799                 renderer.encodeEnd(context, this);
800             }
801         } catch (Exception e) {
802             throw new FacesException("Exception while calling encodeEnd on : "+getPathToComponent(this), e);
803         }
804         finally
805         {
806             setCachedFacesContext(null);
807         }
808     }
809 
810     protected void addFacesListener(FacesListener listener)
811     {
812         if (listener == null) throw new NullPointerException("listener");
813         if (_facesListeners == null)
814         {
815             _facesListeners = new ArrayList();
816         }
817         _facesListeners.add(listener);
818     }
819 
820     protected FacesListener[] getFacesListeners(Class clazz)
821     {
822         if (_facesListeners == null)
823         {
824             return (FacesListener[])Array.newInstance(clazz, 0);
825         }
826         List lst = null;
827         for (Iterator it = _facesListeners.iterator(); it.hasNext(); )
828         {
829             FacesListener facesListener = (FacesListener)it.next();
830             if (clazz.isAssignableFrom(facesListener.getClass()))
831             {
832                 if (lst == null) lst = new ArrayList();
833                 lst.add(facesListener);
834             }
835         }
836         if (lst == null)
837         {
838             return (FacesListener[])Array.newInstance(clazz, 0);
839         }
840         else
841         {
842             return (FacesListener[])lst.toArray((FacesListener[])Array.newInstance(clazz, lst.size()));
843         }
844     }
845 
846     protected void removeFacesListener(FacesListener listener)
847     {
848         if (_facesListeners != null)
849         {
850             _facesListeners.remove(listener);
851         }
852     }
853 
854     public void queueEvent(FacesEvent event)
855     {
856         if (event == null) throw new NullPointerException("event");
857         UIComponent parent = getParent();
858         if (parent == null)
859         {
860             throw new IllegalStateException("component is not a descendant of a UIViewRoot");
861         }
862         parent.queueEvent(event);
863     }
864 
865     public void processDecodes(FacesContext context)
866     {
867         if (context == null)
868             throw new NullPointerException("context");
869         
870         try
871         {
872             setCachedFacesContext(context);
873             
874             if (!isRendered()) return;
875             
876             for (Iterator it = getFacetsAndChildren(); it.hasNext(); )
877             {
878                 UIComponent childOrFacet = (UIComponent)it.next();
879                 childOrFacet.processDecodes(context);
880             }
881             try
882             {
883                 decode(context);
884             }
885             catch (RuntimeException e)
886             {
887                 context.renderResponse();
888                 throw e;
889             }
890         }
891         finally
892         {
893             setCachedFacesContext(null);
894         }
895     }
896 
897 
898     public void processValidators(FacesContext context)
899     {
900         if (context == null) throw new NullPointerException("context");
901         
902         try
903         {
904             setCachedFacesContext(context);
905             if (!isRendered()) return;
906     
907             for (Iterator it = getFacetsAndChildren(); it.hasNext(); )
908             {
909                 UIComponent childOrFacet = (UIComponent)it.next();
910                 childOrFacet.processValidators(context);
911             }
912         }
913         finally
914         {
915             setCachedFacesContext(null);
916         }
917     }
918 
919     /**
920      * This isn't an input component, so just pass on the processUpdates
921      * call to child components and facets that might be input components.
922      * <p>
923      * Components that were never rendered can't possibly be receiving
924      * update data (no corresponding fields were ever put into the response)
925      * so if this component is not rendered then this method does not
926      * invoke processUpdates on its children.
927      */
928     public void processUpdates(FacesContext context)
929     {
930         if (context == null) throw new NullPointerException("context");
931         
932         try
933         {
934             setCachedFacesContext(context);
935             
936             if (!isRendered()) return;
937     
938             for (Iterator it = getFacetsAndChildren(); it.hasNext(); )
939             {
940                 UIComponent childOrFacet = (UIComponent)it.next();
941                 childOrFacet.processUpdates(context);
942             }
943         }
944         finally
945         {
946             setCachedFacesContext(null);
947         }
948     }
949 
950     public Object processSaveState(FacesContext context)
951     {
952         if (context == null) throw new NullPointerException("context");
953         if (isTransient()) return null;
954         Map facetMap = null;
955         for (Iterator it = getFacets().entrySet().iterator(); it.hasNext(); )
956         {
957             Map.Entry entry = (Map.Entry)it.next();
958             if (facetMap == null) facetMap = new HashMap();
959             UIComponent component = (UIComponent)entry.getValue();
960             if (!component.isTransient())
961             {
962                 facetMap.put(entry.getKey(), component.processSaveState(context));
963             }
964         }
965         List childrenList = null;
966         if (getChildCount() > 0)
967         {
968             for (Iterator it = getChildren().iterator(); it.hasNext(); )
969             {
970               UIComponent child = (UIComponent)it.next();
971               if (childrenList == null) {
972                 childrenList = new ArrayList(getChildCount());
973               }
974               Object childState = child.processSaveState(context);
975               if (childState != null) {
976                 childrenList.add(childState);
977               }
978             }
979         }
980         Object savedState;
981         try {
982              savedState = saveState(context);
983         } catch (Exception e) {
984             throw new FacesException("Exception while saving state of component : "+getPathToComponent(this), e);
985         }
986 
987         return new Object[] {savedState,
988                              facetMap,
989                              childrenList};
990     }
991 
992     public void processRestoreState(FacesContext context,
993                                     Object state)
994     {
995         if (context == null) throw new NullPointerException("context");
996         Object myState = ((Object[])state)[0];
997         Map facetMap = (Map)((Object[])state)[1];
998         List childrenList = (List)((Object[])state)[2];
999         if(facetMap != null)
1000         {
1001           for (Iterator it = getFacets().entrySet().iterator(); it.hasNext(); )
1002           {
1003               Map.Entry entry = (Map.Entry)it.next();
1004               Object facetState = facetMap.get(entry.getKey());
1005               if (facetState != null)
1006               {
1007                 UIComponent component = (UIComponent)entry.getValue();
1008                 component.processRestoreState(context, facetState);
1009               }
1010               else
1011               {
1012                   context.getExternalContext().log("No state found to restore facet " + entry.getKey());
1013               }
1014           }
1015         }
1016         if (childrenList != null && getChildCount() > 0)
1017         {
1018             int idx = 0;
1019             for (Iterator it = getChildren().iterator(); it.hasNext(); )
1020             {
1021                 UIComponent child = (UIComponent)it.next();
1022                 if(!child.isTransient())
1023                 {
1024                   Object childState = childrenList.get(idx++);
1025                   if (childState != null)
1026                   {
1027                       child.processRestoreState(context, childState);
1028                   }
1029                   else
1030                   {
1031                       context.getExternalContext().log("No state found to restore child of component " + getId());
1032                   }
1033                 }
1034             }
1035         }
1036         try {
1037             restoreState(context, myState);
1038         } catch (Exception e) {
1039             throw new FacesException("Exception while restoring state of component : "+getPathToComponent(this), e);
1040         }
1041     }
1042 
1043     protected FacesContext getFacesContext()
1044     {
1045         if (_facesContext == null)
1046         {
1047             return FacesContext.getCurrentInstance();
1048         }
1049         else
1050         {
1051             return _facesContext;
1052         }
1053     }
1054 
1055     protected Renderer getRenderer(FacesContext context)
1056     {
1057         if (context == null) throw new NullPointerException("context");
1058         String rendererType = getRendererType();
1059         if (rendererType == null) return null;
1060         
1061         RenderKit renderKit = context.getRenderKit();
1062         Renderer renderer = renderKit.getRenderer(getFamily(), rendererType);
1063         if (renderer == null)
1064         {
1065             getFacesContext().getExternalContext().log("No Renderer found for component " + getPathToComponent(this) + " (component-family=" + getFamily() + ", renderer-type=" + rendererType + ")");
1066             log.warn("No Renderer found for component " + getPathToComponent(this) + " (component-family=" + getFamily() + ", renderer-type=" + rendererType + ")");
1067         }
1068         return renderer;
1069     }
1070 
1071     private String getPathToComponent(UIComponent component)
1072     {
1073         StringBuffer buf = new StringBuffer();
1074 
1075         if(component == null)
1076         {
1077             buf.append("{Component-Path : ");
1078             buf.append("[null]}");
1079             return buf.toString();
1080         }
1081 
1082         getPathToComponent(component,buf);
1083 
1084         buf.insert(0,"{Component-Path : ");
1085         buf.append("}");
1086 
1087         return buf.toString();
1088     }
1089 
1090     private static void getPathToComponent(UIComponent component, StringBuffer buf)
1091     {
1092         if(component == null)
1093             return;
1094 
1095         StringBuffer intBuf = new StringBuffer();
1096 
1097         intBuf.append("[Class: ");
1098         intBuf.append(component.getClass().getName());
1099         if(component instanceof UIViewRoot)
1100         {
1101             intBuf.append(",ViewId: ");
1102             intBuf.append(((UIViewRoot) component).getViewId());
1103         }
1104         else
1105         {
1106             intBuf.append(",Id: ");
1107             intBuf.append(component.getId());
1108         }
1109         intBuf.append("]");
1110 
1111         buf.insert(0,intBuf.toString());
1112 
1113         getPathToComponent(component.getParent(), buf);
1114     }
1115 
1116     /**
1117      * @JSFProperty
1118      *   literalOnly = "true"
1119      *   transient = "true"
1120      *   tagExcluded ="true"
1121      */
1122     public boolean isTransient()
1123     {
1124         return _transient;
1125     }
1126 
1127     public void setTransient(boolean transientFlag)
1128     {
1129         _transient = transientFlag;
1130     }
1131 
1132     /**
1133      * Serializes objects which are "attached" to this component but which are
1134      * not UIComponent children of it. Examples are validator and listener
1135      * objects. To be precise, it returns an object which implements
1136      * java.io.Serializable, and which when serialized will persist the
1137      * state of the provided object.     
1138      * <p>
1139      * If the attachedObject is a List then every object in the list is saved
1140      * via a call to this method, and the returned wrapper object contains
1141      * a List object.
1142      * <p>
1143      * If the object implements StateHolder then the object's saveState is
1144      * called immediately, and a wrapper is returned which contains both
1145      * this saved state and the original class name. However in the case
1146      * where the StateHolder.isTransient method returns true, null is
1147      * returned instead.
1148      * <p>
1149      * If the object implements java.io.Serializable then the object is simply
1150      * returned immediately; standard java serialization will later be used
1151      * to store this object.
1152      * <p>
1153      * In all other cases, a wrapper is returned which simply stores the type
1154      * of the provided object. When deserialized, a default instance of that
1155      * type will be recreated.
1156      */
1157     public static Object saveAttachedState(FacesContext context,
1158                                            Object attachedObject)
1159     {
1160         if (attachedObject == null) return null;
1161         if (attachedObject instanceof List)
1162         {
1163             List lst = new ArrayList(((List)attachedObject).size());
1164             for (Iterator it = ((List)attachedObject).iterator(); it.hasNext(); )
1165             {
1166                 Object value = it.next();
1167                 if (value != null)
1168                 {
1169                     lst.add(saveAttachedState(context, value));
1170                 }
1171             }
1172             return new _AttachedListStateWrapper(lst);
1173         }
1174         else if (attachedObject instanceof StateHolder)
1175         {
1176             if (((StateHolder)attachedObject).isTransient())
1177             {
1178                 return null;
1179             }
1180             else
1181             {
1182                 return new _AttachedStateWrapper(attachedObject.getClass(),
1183                                                  ((StateHolder)attachedObject).saveState(context));
1184             }
1185         }
1186         else if (attachedObject instanceof Serializable)
1187         {
1188             return attachedObject;
1189         }
1190         else
1191         {
1192             return new _AttachedStateWrapper(attachedObject.getClass(), null);
1193         }
1194     }
1195 
1196     public static Object restoreAttachedState(FacesContext context,
1197                                               Object stateObj)
1198             throws IllegalStateException
1199     {
1200         if (context == null) throw new NullPointerException("context");
1201         if (stateObj == null) return null;
1202         if (stateObj instanceof _AttachedListStateWrapper)
1203         {
1204             List lst = ((_AttachedListStateWrapper)stateObj).getWrappedStateList();
1205             List restoredList = new ArrayList(lst.size());
1206             for (Iterator it = lst.iterator(); it.hasNext(); )
1207             {
1208                 restoredList.add(restoreAttachedState(context, it.next()));
1209             }
1210             return restoredList;
1211         }
1212         else if (stateObj instanceof _AttachedStateWrapper)
1213         {
1214             Class clazz = ((_AttachedStateWrapper)stateObj).getClazz();
1215             Object restoredObject;
1216             try
1217             {
1218                 restoredObject = clazz.newInstance();
1219             }
1220             catch (InstantiationException e)
1221             {
1222                 throw new RuntimeException("Could not restore StateHolder of type " + clazz.getName() + " (missing no-args constructor?)", e);
1223             }
1224             catch (IllegalAccessException e)
1225             {
1226                 throw new RuntimeException(e);
1227             }
1228             if (restoredObject instanceof StateHolder)
1229             {
1230                 Object wrappedState = ((_AttachedStateWrapper)stateObj).getWrappedStateObject();
1231                 ((StateHolder)restoredObject).restoreState(context, wrappedState);
1232             }
1233             return restoredObject;
1234         }
1235         else
1236         {
1237             return stateObj;
1238         }
1239     }
1240 
1241 
1242     /**
1243      * Invoked after the render phase has completed, this method
1244      * returns an object which can be passed to the restoreState
1245      * of some other instance of UIComponentBase to reset that
1246      * object's state to the same values as this object currently
1247      * has.
1248      */
1249     public Object saveState(FacesContext context)
1250     {
1251         Object values[] = new Object[7];
1252         values[0] = _id;
1253         values[1] = _rendered;
1254         values[2] = _rendererType;
1255         values[3] = _clientId;
1256         values[4] = saveAttributesMap();
1257         values[5] = saveAttachedState(context, _facesListeners);
1258         values[6] = saveValueBindingMap(context);
1259         return values;
1260     }
1261 
1262     /**
1263      * Invoked in the "restore view" phase, this initialises this
1264      * object's members from the values saved previously into the
1265      * provided state object.
1266      * <p>
1267      * @param state is an object previously returned by
1268      * the saveState method of this class.
1269      */
1270     public void restoreState(FacesContext context, Object state)
1271     {
1272         Object values[] = (Object[])state;
1273         _id = (String)values[0];
1274         _rendered = (Boolean)values[1];
1275         _rendererType = (String)values[2];
1276         _clientId = (String)values[3];
1277         restoreAttributesMap(values[4]);
1278         _facesListeners = (List)restoreAttachedState(context, values[5]);
1279         restoreValueBindingMap(context, values[6]);
1280     }
1281 
1282 
1283     private Object saveAttributesMap()
1284     {
1285         if (_attributesMap != null)
1286         {
1287             return _attributesMap.getUnderlyingMap();
1288         }
1289         else
1290         {
1291             return null;
1292         }
1293     }
1294 
1295     private void restoreAttributesMap(Object stateObj)
1296     {
1297         if (stateObj != null)
1298         {
1299             _attributesMap = new _ComponentAttributesMap(this, (Map)stateObj);
1300         }
1301         else
1302         {
1303             _attributesMap = null;
1304         }
1305     }
1306 
1307     private Object saveValueBindingMap(FacesContext context)
1308     {
1309         if (_valueBindingMap != null)
1310         {
1311             int initCapacity = (_valueBindingMap.size() * 4 + 3) / 3;
1312             HashMap stateMap = new HashMap(initCapacity);
1313             for (Iterator it = _valueBindingMap.entrySet().iterator(); it.hasNext(); )
1314             {
1315                 Map.Entry entry = (Map.Entry)it.next();
1316                 stateMap.put(entry.getKey(),
1317                              saveAttachedState(context, entry.getValue()));
1318             }
1319             return stateMap;
1320         }
1321         else
1322         {
1323             return null;
1324         }
1325     }
1326 
1327     private void restoreValueBindingMap(FacesContext context, Object stateObj)
1328     {
1329         if (stateObj != null)
1330         {
1331             Map stateMap = (Map)stateObj;
1332             int initCapacity = (stateMap.size() * 4 + 3) / 3;
1333             _valueBindingMap = new HashMap(initCapacity);
1334             for (Iterator it = stateMap.entrySet().iterator(); it.hasNext(); )
1335             {
1336                 Map.Entry entry = (Map.Entry)it.next();
1337                 _valueBindingMap.put(entry.getKey(),
1338                                      restoreAttachedState(context, entry.getValue()));
1339             }
1340         }
1341         else
1342         {
1343             _valueBindingMap = null;
1344         }
1345     }
1346 
1347 
1348     /**
1349      * @param string the component id, that should be a vaild one.
1350      */
1351     private void isIdValid(String string)
1352     {
1353 
1354         //is there any component identifier ?
1355         if(string == null)
1356             return;
1357 
1358         //Component identifiers must obey the following syntax restrictions:
1359         //1. Must not be a zero-length String.
1360         if(string.length()==0)
1361         {
1362             throw new IllegalArgumentException("component identifier must not be a zero-length String");
1363         }
1364 
1365         //let's look at all chars inside of the ID if it is a valid ID!
1366         char[] chars = string.toCharArray();
1367 
1368         //2. First character must be a letter or an underscore ('_').
1369         if(!Character.isLetter(chars[0]) &&  chars[0] !='_')
1370         {
1371             throw new IllegalArgumentException("component identifier's first character must be a letter or an underscore ('_')! But it is \""+chars[0]+"\"");
1372         }
1373         for (int i = 1; i < chars.length; i++)
1374         {
1375             //3. Subsequent characters must be a letter, a digit, an underscore ('_'), or a dash ('-').
1376             if(!Character.isDigit(chars[i]) && !Character.isLetter(chars[i]) && chars[i] !='-' && chars[i] !='_')
1377             {
1378                 throw new IllegalArgumentException("Subsequent characters of component identifier must be a letter, a digit, an underscore ('_'), or a dash ('-')! But component identifier contains \""+chars[i]+"\"");
1379             }
1380         }
1381     }
1382 
1383     boolean isCachedFacesContext()
1384     {
1385         return _facesContext != null;
1386     }
1387     
1388     void setCachedFacesContext(FacesContext facesContext)
1389     {
1390         _facesContext = facesContext;
1391     }
1392 
1393     //------------------ GENERATED CODE BEGIN (do not modify!) --------------------
1394 
1395     private static final boolean DEFAULT_RENDERED = true;
1396 
1397     private Boolean _rendered = null;
1398     private String _rendererType = null;
1399 
1400 
1401 
1402     public void setRendered(boolean rendered)
1403     {
1404         _rendered = Boolean.valueOf(rendered);
1405     }
1406 
1407     /**
1408      * A boolean value that indicates whether this component should be rendered.
1409      * Default value: true.
1410      * 
1411      * @JSFProperty
1412      */
1413     public boolean isRendered()
1414     {
1415         if (_rendered != null) return _rendered.booleanValue();
1416         ValueBinding vb = getValueBinding("rendered");
1417         Boolean v = vb != null ? (Boolean)vb.getValue(getFacesContext()) : null;
1418         return v != null ? v.booleanValue() : DEFAULT_RENDERED;
1419     }
1420 
1421     public void setRendererType(String rendererType)
1422     {
1423         _rendererType = rendererType;
1424     }
1425 
1426     public String getRendererType()
1427     {
1428         if (_rendererType != null) return _rendererType;
1429         ValueBinding vb = getValueBinding("rendererType");
1430         return vb != null ? _ComponentUtils.getStringValue(getFacesContext(), vb) : null;
1431     }
1432 
1433 
1434     //------------------ GENERATED CODE END ---------------------------------------
1435 }