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