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 org.apache.myfaces.trinidad.component;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.InputStream;
23  import java.io.IOException;
24  import java.io.ObjectOutputStream;
25  
26  import java.net.URL;
27  import java.util.Iterator;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.NoSuchElementException;
31  import java.util.Properties;
32  
33  import javax.el.ELContext;
34  import javax.el.ELException;
35  import javax.el.MethodExpression;
36  import javax.el.ValueExpression;
37  
38  import javax.faces.component.NamingContainer;
39  import javax.faces.component.UIComponent;
40  import javax.faces.context.FacesContext;
41  import javax.faces.el.EvaluationException;
42  import javax.faces.el.MethodBinding;
43  import javax.faces.el.ValueBinding;
44  import javax.faces.event.AbortProcessingException;
45  import javax.faces.event.FacesEvent;
46  import javax.faces.event.FacesListener;
47  import javax.faces.render.RenderKit;
48  import javax.faces.render.Renderer;
49  
50  import org.apache.myfaces.trinidad.bean.FacesBean;
51  import org.apache.myfaces.trinidad.bean.FacesBeanFactory;
52  import org.apache.myfaces.trinidad.bean.PropertyKey;
53  import org.apache.myfaces.trinidad.bean.util.StateUtils;
54  import org.apache.myfaces.trinidad.bean.util.ValueMap;
55  import org.apache.myfaces.trinidad.change.AttributeComponentChange;
56  import org.apache.myfaces.trinidad.context.RequestContext;
57  import org.apache.myfaces.trinidad.event.AttributeChangeEvent;
58  import org.apache.myfaces.trinidad.event.AttributeChangeListener;
59  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
60  import org.apache.myfaces.trinidad.render.ExtendedRenderer;
61  import org.apache.myfaces.trinidad.render.LifecycleRenderer;
62  
63  
64  /**
65   * Base implementation of components for all of Trinidad.  UIXComponentBase
66   * offers a number of features not supplied by the standard UIComponentBase
67   * class:
68   * <ul>
69   * <li>Use of FacesBean for better and easier state saving</li>
70   * <li>Support of the LifecycleRenderer class for greater Renderer
71   *  control over the lifecycle</li>
72   * <li>Built-in support for both the "partialTriggers" attribute
73   *   (declarative support for being a PPR target) and for triggering
74   *   such components (for being a the source of a PPR-causing event).</li>
75   * </ul>
76   * <h3>FacesBean and UIXComponentBase</h3>
77   * <p>
78   * UIXComponentBase differs from UIXComponent most particularly
79   * in its use of FacesBeans to store all state.  This offers
80   * a number of advantages:
81   * <ul>
82   * <li>Subclassers - if they use FacesBean for their state as well -
83   *   do not need to write overrides of saveState() and restoreState().
84   *   </li>
85   * <li>State is optimized by default</li>
86   * <li>Future optimizations - partly exposed today with
87   *    markInitialState() - can offer major state saving improvements.
88   * </ul>
89   * </p>
90   */
91  // TODO Write Class Javadoc
92  // TODO Thorough review against UIComponentBase
93  abstract public class UIXComponentBase extends UIXComponent
94  {
95    // Created up top to ensure it's present while we're processing
96    // class initialization code.
97    static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(UIXComponentBase.class);
98  
99    static public final FacesBean.Type TYPE = _createType();
100   static public final PropertyKey ID_KEY =
101     TYPE.registerKey("id", String.class, PropertyKey.CAP_NOT_BOUND);
102   static private final PropertyKey _GENERATED_ID_KEY =
103     TYPE.registerKey("_genId", String.class, PropertyKey.CAP_NOT_BOUND);
104   static public final PropertyKey RENDERED_KEY =
105     TYPE.registerKey("rendered", Boolean.class, Boolean.TRUE);
106   static public final PropertyKey BINDING_KEY =
107     TYPE.registerKey("binding");
108   static public final PropertyKey TRANSIENT_KEY =
109     TYPE.registerKey("transient", Boolean.class,
110                      PropertyKey.CAP_NOT_BOUND |
111                      PropertyKey.CAP_TRANSIENT);
112   static public final PropertyKey RENDERER_TYPE_KEY =
113     TYPE.registerKey("rendererType", String.class, PropertyKey.CAP_NOT_BOUND);
114   static private final PropertyKey _LISTENERS_KEY =
115     TYPE.registerKey("listeners", FacesListener[].class, PropertyKey.CAP_LIST);
116   static private final PropertyKey _ATTRIBUTE_CHANGE_LISTENER_KEY =
117     TYPE.registerKey("attributeChangeListener", MethodExpression.class);
118   // =-=AEW "parent", "rendersChildren", "childCount", "children",
119   // "facets", "facetsAndChildren", "family" all are technically
120   // bean properties, but they aren't exposed here...
121 
122   static
123   {
124     // Register a couple of PropertyKeys against names that
125     // the RI's UIComponentTag implementation is shoving
126     // into all components.  This is purely an optimization, but
127     // a very useful one.
128     TYPE.registerKey("javax.faces.webapp.COMPONENT_IDS",
129                      List.class,
130                      PropertyKey.CAP_NOT_BOUND);
131     TYPE.registerKey("javax.faces.webapp.FACET_NAMES",
132                      List.class,
133                      PropertyKey.CAP_NOT_BOUND);
134     TYPE.lock();
135   }
136 
137   public UIXComponentBase()
138   {
139   }
140 
141   public UIXComponentBase(String rendererType)
142   {
143     setRendererType(rendererType);
144   }
145 
146   protected FacesBean createFacesBean(
147     String rendererType)
148   {
149     FacesBean bean = FacesBeanFactory.createFacesBean(getClass(),
150                                                       rendererType);
151     UIXFacesBean uixBean = (UIXFacesBean) bean;
152     uixBean.init(this, getBeanType());
153     return uixBean;
154   }
155 
156   protected PropertyKey getPropertyKey(String name)
157   {
158     PropertyKey key = getBeanType().findKey(name);
159     if (key == null)
160       key = PropertyKey.createPropertyKey(name);
161 
162     return key;
163   }
164 
165   protected FacesBean.Type getBeanType()
166   {
167     return TYPE;
168   }
169 
170   @Override
171   public FacesBean getFacesBean()
172   {
173     if (_facesBean == null)
174       _init(null);
175 
176     return _facesBean;
177   }
178 
179   @Override
180   public String getContainerClientId(FacesContext context, UIComponent child)
181   {
182     return getContainerClientId(context);
183   }
184 
185   @Override
186   public void addAttributeChangeListener(AttributeChangeListener acl)
187   {
188     addFacesListener(acl);
189   }
190 
191   @Override
192   public void removeAttributeChangeListener(AttributeChangeListener acl)
193   {
194     removeFacesListener(acl);
195   }
196 
197   @Override
198   public AttributeChangeListener[] getAttributeChangeListeners()
199   {
200     return (AttributeChangeListener[])
201       getFacesListeners(AttributeChangeListener.class);
202   }
203 
204   @Override
205   public void setAttributeChangeListener(MethodExpression mb)
206   {
207     setProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY, mb);
208   }
209 
210   @Deprecated
211   public void setAttributeChangeListener(MethodBinding mb)
212   {
213     setAttributeChangeListener(adaptMethodBinding(mb));
214   }
215 
216   @Override
217   public MethodExpression getAttributeChangeListener()
218   {
219     return (MethodExpression) getProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY);
220   }
221 
222 
223   @Override
224   public ValueExpression getValueExpression(String name)
225   {
226     if (name == null)
227       throw new NullPointerException();
228 
229     PropertyKey key = getPropertyKey(name);
230 
231     // Support standard RI behavior where getValueBinding()
232     // doesn't complain about being asked for a ValueBinding -
233     // but continue supporting strict behavior at FacesBean layer.
234     if (!key.getSupportsBinding())
235       return null;
236 
237     return getFacesBean().getValueExpression(key);
238   }
239 
240   @Override
241   public void setValueExpression(String name,
242                                  ValueExpression expression)
243   {
244     if (name == null)
245       throw new NullPointerException();
246 
247     if ((expression != null) && expression.isLiteralText())
248     {
249       ELContext context =
250           FacesContext.getCurrentInstance().getELContext();
251       getAttributes().put(name, expression.getValue(context));
252     }
253     else
254     {
255       PropertyKey key = getPropertyKey(name);
256       getFacesBean().setValueExpression(key, expression);
257     }
258   }
259 
260 
261 
262   /**
263    */
264   @Override
265   public ValueBinding getValueBinding(String name)
266   {
267     if (name == null)
268       throw new NullPointerException();
269 
270     PropertyKey key = getPropertyKey(name);
271 
272     // Support standard RI behavior where getValueBinding()
273     // doesn't complain about being asked for a ValueBinding -
274     // but continue supporting strict behavior at FacesBean layer.
275     if (!key.getSupportsBinding())
276       return null;
277 
278     return getFacesBean().getValueBinding(key);
279   }
280 
281 
282   @Override
283   public void setValueBinding(String name, ValueBinding binding)
284   {
285     if (name == null)
286       throw new NullPointerException();
287 
288     PropertyKey key = getPropertyKey(name);
289     getFacesBean().setValueBinding(key, binding);
290   }
291 
292 
293   @Override
294   public Map<String, Object> getAttributes()
295   {
296     if (_attributes == null)
297       _init(null);
298 
299     return _attributes;
300   }
301 
302   // ------------------------------------------------------------- Properties
303 
304 
305 
306   @Override
307   public String getClientId(FacesContext context)
308   {
309     // NOTE - client ids cannot be cached because the generated
310     // value has to be dynamically calculated in some cases (UIData)
311 
312     String clientId = getId();
313     if (clientId == null)
314     {
315       clientId = (String) getProperty(_GENERATED_ID_KEY);
316       if (clientId == null)
317       {
318         clientId = context.getViewRoot().createUniqueId();
319         setProperty(_GENERATED_ID_KEY, clientId);
320       }
321     }
322 
323     // Search for an ancestor that is a naming container
324     UIComponent containerComponent = getParent();
325     while (null != containerComponent)
326     {
327       if (containerComponent instanceof NamingContainer)
328       {
329         String contClientId;
330 
331         // Pass additional context information to naming containers which extend UIXComponent:
332         if (containerComponent instanceof UIXComponent)
333           contClientId = ((UIXComponent)containerComponent).getContainerClientId(context, this);
334         else
335           contClientId = containerComponent.getContainerClientId(context);
336 
337         StringBuilder bld = __getSharedStringBuilder();
338         bld.append(contClientId).append(NamingContainer.SEPARATOR_CHAR).append(clientId);
339         clientId = bld.toString();
340         break;
341       }
342 
343       containerComponent = containerComponent.getParent();
344     }
345 
346     Renderer renderer = getRenderer(context);
347     if (null != renderer)
348       clientId = renderer.convertClientId(context, clientId);
349 
350     return clientId;
351   }
352 
353 
354   /**
355    * Gets the identifier for the component.
356    */
357   @Override
358   public String getId()
359   {
360     return (String) getProperty(ID_KEY);
361   }
362 
363 
364   /**
365    * Sets the identifier for the component.  The identifier
366    * must follow a subset of the syntax allowed in HTML:
367    * <ul>
368    * <li>Must not be a zero-length String.</li>
369    * <li>First character must be an ASCII letter (A-Za-z) or an underscore ('_').</li>
370    * <li>Subsequent characters must be an ASCII letter or digit (A-Za-z0-9), an underscore ('_'), or a dash ('-').
371    * </ul>
372    */
373   @Override
374   public void setId(String id)
375   {
376     // =-=AEW Currently, setId() assumes that resetting to
377     // the same value *is not* short-circuited.
378     _validateId(id);
379     // If we're setting the ID to null, don't discard
380     // the _GENERATED_ID
381     if (id != null)
382       setProperty(_GENERATED_ID_KEY, null);
383 
384     setProperty(ID_KEY, id);
385   }
386 
387 
388 
389   @Override
390   abstract public String getFamily();
391 
392 
393   @Override
394   public UIComponent getParent()
395   {
396     return _parent;
397   }
398 
399 
400   /**
401    * <p>Set the parent <code>UIComponent</code> of this
402    * <code>UIComponent</code>.</p>
403    *
404    * @param parent The new parent, or <code>null</code> for the root node
405    *  of a component tree
406    */
407   @Override
408   public void setParent(UIComponent parent)
409   {
410     _parent = parent;
411   }
412 
413 
414   @Override
415   public boolean isRendered()
416   {
417     return getBooleanProperty(RENDERED_KEY, true);
418   }
419 
420 
421   @Override
422   public void setRendered(boolean rendered)
423   {
424     setBooleanProperty(RENDERED_KEY, rendered);
425   }
426 
427   public boolean isTransient()
428   {
429     return getBooleanProperty(TRANSIENT_KEY, false);
430   }
431 
432   public void setTransient(boolean newTransient)
433   {
434     setBooleanProperty(TRANSIENT_KEY, newTransient);
435   }
436 
437   @Override
438   public String getRendererType()
439   {
440     // Don't create the FacesBean just to get the renderer type;
441     // Generally, rendererType will be the first property
442     // set, which will trigger the code below in setRendererType()
443     // to run correctly.
444     if (_facesBean == null)
445       return null;
446 
447     return (String) getProperty(RENDERER_TYPE_KEY);
448   }
449 
450   @Override
451   public void setRendererType(String rendererType)
452   {
453     String oldRendererType = getRendererType();
454     if (oldRendererType == null)
455     {
456       if (rendererType == null)
457         return;
458     }
459     else if (oldRendererType.equals(rendererType))
460     {
461       return;
462     }
463 
464     // prepare the faces bean
465     _init(rendererType);
466     setProperty(RENDERER_TYPE_KEY, rendererType);
467   }
468 
469 
470   @Override
471   public boolean getRendersChildren()
472   {
473     Renderer renderer = getRenderer(getFacesContext());
474     if (renderer == null)
475       return false;
476 
477     return renderer.getRendersChildren();
478   }
479 
480 
481 
482 
483   // ------------------------------------------------ Tree Management Methods
484 
485 
486 
487   @Override
488   public UIComponent findComponent(String id)
489   {
490     if (id == null)
491       throw new NullPointerException();
492 
493     if ("".equals(id))
494       throw new IllegalArgumentException();
495 
496     UIComponent from = this;
497     // If it starts with the separator character, it's
498     // an absolute path: start at the top
499     if (id.charAt(0) == NamingContainer.SEPARATOR_CHAR)
500     {
501       id = id.substring(1);
502       while (from.getParent() != null)
503         from = from.getParent();
504     }
505     // If it's a NamingContainer, start right here
506     else if (this instanceof NamingContainer)
507     {
508       ;
509     }
510     // Otherwise, go up to look for NamingContainer (or the top)
511     else
512     {
513       while (from.getParent() != null)
514       {
515         from = from.getParent();
516         if (from instanceof NamingContainer)
517           break;
518       }
519     }
520 
521     // Evaluate each part of the expression
522     String searchId;
523     int separatorIndex = id.indexOf(NamingContainer.SEPARATOR_CHAR);
524     if (separatorIndex < 0)
525       searchId = id;
526     else
527       searchId = id.substring(0, separatorIndex);
528 
529     if (searchId.equals(from.getId()))
530     {
531       // Don't need to look inside if we're already there
532       ;
533     }
534     else
535     {
536       from = _findInsideOf(from, searchId);
537     }
538 
539     // Final ID:  break, and return whatever we've found
540     if (separatorIndex < 0)
541     {
542       return from;
543     }
544     // Just an intermediate step.  Make sure we're at a NamingContainer,
545     // and then ask it to find the rest of the expression.
546     else
547     {
548       if (from == null)
549         return null;
550 
551       if (!(from instanceof NamingContainer))
552         throw new IllegalArgumentException();
553       return from.findComponent(id.substring(separatorIndex + 1));
554     }
555   }
556 
557 
558 
559   /**
560    * <p>Create (if necessary) and return a List of the children associated
561    * with this component.</p>
562    */
563   @Override
564   public List<UIComponent> getChildren()
565   {
566     if (_children == null)
567       _children = new ChildArrayList(this);
568 
569     return _children;
570   }
571 
572   @Override
573   public int getChildCount()
574   {
575     if (_children == null)
576       return 0;
577     return getChildren().size();
578   }
579 
580 
581   /**
582    * <p>Create (if necessary) and return a Map of the facets associated
583    * with this component.</p>
584    */
585   @Override
586   public Map<String, UIComponent> getFacets()
587   {
588 
589     if (_facets == null)
590       _facets = new FacetHashMap(this);
591 
592     return _facets;
593   }
594 
595 
596   @Override
597   public UIComponent getFacet(String facetName)
598   {
599     if (facetName == null)
600       throw new NullPointerException();
601     if (_facets == null)
602       return null;
603     return getFacets().get(facetName);
604   }
605 
606 
607   /**
608    * Returns an Iterator over the names of all facets.
609    * Unlike getFacets().keySet().iterator(), this does
610    * not require instantiating a Map if there are
611    * no facets.  (Note that this is not part of the
612    * UIComponent API.)
613    */
614   public Iterator<String> getFacetNames()
615   {
616     if (_facets == null)
617       return _EMPTY_STRING_ITERATOR;
618     return _facets.keySet().iterator();
619   }
620 
621   @Override
622   public Iterator<UIComponent> getFacetsAndChildren()
623   {
624     // =-=AEW Is this supposed to be an immutable Iterator?
625     if (_facets == null)
626     {
627       if (_children == null)
628         return _EMPTY_UICOMPONENT_ITERATOR;
629 
630       return _children.iterator();
631     }
632     else
633     {
634       if (_children == null)
635         return _facets.values().iterator();
636     }
637     
638     return new CompositeIterator<UIComponent>(_children.iterator(), _facets.values().iterator());
639   }
640 
641   // ------------------------------------------- Event processing methods
642 
643   @Override
644   public void broadcast(FacesEvent event)
645     throws AbortProcessingException
646   {
647     if (event == null)
648       throw new NullPointerException();
649 
650     if (_LOG.isFine())
651       _LOG.fine("Broadcasting event " + event + " to " + this);
652 
653     UIComponent component = event.getComponent();
654     if (component != null)
655     {
656       RequestContext adfContext = RequestContext.getCurrentInstance();
657       if (adfContext != null)
658         adfContext.partialUpdateNotify(component);
659     }
660 
661     Iterator<FacesListener> iter =
662       (Iterator<FacesListener>)getFacesBean().entries(_LISTENERS_KEY);
663 
664     while (iter.hasNext())
665     {
666       FacesListener listener = iter.next();
667       if (event.isAppropriateListener(listener))
668       {
669         event.processListener(listener);
670       }
671     }
672 
673     if (event instanceof AttributeChangeEvent)
674     {
675       broadcastToMethodExpression(event, getAttributeChangeListener());
676     }
677   }
678 
679 
680   // ------------------------------------------- Lifecycle Processing Methods
681 
682 
683   @Override
684   public void decode(FacesContext context)
685   {
686     if (context == null)
687       throw new NullPointerException();
688 
689     // Find all the partialTriggers and save on the context
690     Map<String, Object> attrs = getAttributes();
691     Object triggers = attrs.get("partialTriggers");
692     if (triggers instanceof String[])
693     {
694       RequestContext adfContext = RequestContext.getCurrentInstance();
695       if (adfContext != null)
696         adfContext.addPartialTriggerListeners(this, (String[]) triggers);
697     }
698 
699     __rendererDecode(context);
700   }
701 
702   @Override
703   public void encodeBegin(FacesContext context) throws IOException
704   {
705     if (context == null)
706       throw new NullPointerException();
707 
708     if (!isRendered())
709       return;
710 
711     _cacheRenderer(context);
712     Renderer renderer = getRenderer(context);
713     // if there is a Renderer for this component
714     if (renderer != null)
715     {
716       renderer.encodeBegin(context, this);
717     }
718   }
719 
720   @Override
721   public void encodeChildren(FacesContext context) throws IOException
722   {
723     if (context == null)
724       throw new NullPointerException();
725 
726     if (!isRendered())
727       return;
728 
729     Renderer renderer = getRenderer(context);
730     // if there is a Renderer for this component
731     if (renderer != null)
732     {
733       renderer.encodeChildren(context, this);
734     }
735   }
736 
737   @Override
738   public void encodeEnd(FacesContext context) throws IOException
739   {
740     if (context == null)
741       throw new NullPointerException();
742 
743     if (isRendered())
744     {
745       Renderer renderer = getRenderer(context);
746       // if there is a Renderer for this component
747       if (renderer != null)
748       {
749         renderer.encodeEnd(context, this);
750       }
751     }
752   }
753 
754   /**
755    * Encodes a component and all of its children, whether
756    * getRendersChildren() is true or false.  When rendersChildren
757    * is false, each child whose "rendered" property is true
758    * will be sequentially rendered;  facets will be ignored.
759    */
760   @Override
761   public void encodeAll(FacesContext context) throws IOException
762   {
763     if (context == null)
764       throw new NullPointerException();
765 
766     // This code ends up calling isRendered() once overall,
767     // plus up to three times more for encodeBegin(),
768     // encodeChildren(), and encodeEnd().
769     __encodeRecursive(context, this);
770   }
771 
772   @Override
773   public void queueEvent(FacesEvent event)
774   {
775     if (event == null)
776       throw new NullPointerException();
777 
778     UIComponent parent = getParent();
779     if (parent == null)
780       throw new IllegalStateException();
781 
782     parent.queueEvent(event);
783   }
784 
785   // ----------------------------------------------- Lifecycle Phase Handlers
786 
787   @Override
788   public void processDecodes(FacesContext context)
789   {
790     if (context == null)
791       throw new NullPointerException();
792 
793     if (!isRendered())
794       return;
795 
796     // Process all facets and children of this component
797     decodeChildren(context);
798 
799     // Process this component itself
800     decode(context);
801 
802   }
803 
804   @Override
805   public void processValidators(FacesContext context)
806   {
807     if (context == null)
808       throw new NullPointerException();
809 
810     if (!isRendered())
811       return;
812 
813     // Process all facets and children of this component
814     validateChildren(context);
815   }
816 
817   @Override
818   public void processUpdates(FacesContext context)
819   {
820     if (context == null)
821       throw new NullPointerException();
822 
823     if (!isRendered())
824       return;
825 
826     // Process all facets and children of this component
827     updateChildren(context);
828   }
829 
830   @Override
831   public Object processSaveState(FacesContext context)
832   {
833     if (context == null)
834       throw new NullPointerException();
835 
836     if (_LOG.isFiner())
837       _LOG.finer("processSaveState() on " + this);
838     
839     Object state = null;
840     
841     try
842     {
843       if (((_children == null) || _children.isEmpty()) &&
844           ((_facets == null) || _facets.isEmpty()))
845       {
846         state = saveState(context);
847       }
848       else
849       {
850         TreeState treeState = new TreeState();
851         treeState.saveState(context, this);
852         if (treeState.isEmpty())
853           state = null;
854   
855         state = treeState;
856       }
857     }
858     catch (RuntimeException e)
859     {
860       _LOG.warning(_LOG.getMessage("COMPONENT_CHILDREN_SAVED_STATE_FAILED", this));
861       
862       throw e;
863     }
864     
865     // if component state serialization checking is on, attempt to Serialize the
866     // component state immediately in order to determine which component's state
867     // failed state saving.  Note that since our parent will attempt this same
868     // serialization, turning this on is expensive and should only be used once
869     // a serialization error has been detected and we want to know which
870     // component's state failed
871     if (StateUtils.checkComponentStateSerialization(context))
872     {
873       try
874       {
875         new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(state);  
876       }
877       catch (IOException e)
878       {
879         throw new RuntimeException(_LOG.getMessage("COMPONENT_SAVED_STATE_FAILED", this), e);
880       }
881     }
882 
883     return state;
884   }
885 
886   // TODO  will have deep problems if UIComponent.saveState() ever
887   //   returns a String.
888   // TODO crashes and burns if there are fewer children or missing
889   //  facets from when state was saved.
890   @Override
891   public void processRestoreState(FacesContext context, Object state)
892   {
893     if (context == null)
894       throw new NullPointerException();
895 
896     if (_LOG.isFiner())
897       _LOG.finer("processRestoreState() on " + this);
898 
899     // If we saved a "TreeState", use it to restore everything
900     if (state instanceof TreeState)
901     {
902       ((TreeState) state).restoreState(context, this);
903     }
904     // Otherwise, we had no children or facets, and just use
905     // the "state" object
906     else
907     {
908       restoreState(context, state);
909     }
910   }
911 
912   @Override
913   public void markInitialState()
914   {
915     // -= Simon Lessard =-
916     // FIXME: Set to true, but never read
917     //_initialStateMarked = true;
918     getFacesBean().markInitialState();
919   }
920 
921   public Object saveState(FacesContext context)
922   {
923     return getFacesBean().saveState(context);
924   }
925 
926   public void restoreState(FacesContext context, Object stateObj)
927   {
928     getFacesBean().restoreState(context, stateObj);
929   }
930 
931 
932   @Override
933   public String toString()
934   {
935     String className = getClass().getName();
936     int periodIndex = className.lastIndexOf('.');
937     if (periodIndex >= 0)
938       className = className.substring(periodIndex + 1);
939 
940     return className + "[" + getFacesBean().toString() + ", id=" + getId() + "]";
941   }
942 
943   /**
944    * <p>Return the {@link FacesContext} instance for the current request.</p>
945    */
946   @Override
947   protected FacesContext getFacesContext()
948   {
949     // If we ever have a way for a component to get notified
950     // when it's finished being used for a given request,
951     // we could cache this as an instance variable.
952     return FacesContext.getCurrentInstance();
953   }
954 
955 
956   /**
957    * Delegates to LifecycleRenderer, if present,
958    * otherwise calls decodeChildrenImpl.
959    *
960    * @param context the current FacesContext
961    */
962   final protected void decodeChildren(FacesContext context)
963   {
964     LifecycleRenderer renderer = getLifecycleRenderer(context);
965     // if there is a HierarchyRenderer for this component
966     if (renderer != null)
967     {
968       if (renderer.decodeChildren(context, this))
969         return;
970     }
971 
972     decodeChildrenImpl(context);
973   }
974 
975   /**
976    * Calls processDecodes on all facets and children of this
977    * component.
978    * @param context the current FacesContext
979    */
980   protected void decodeChildrenImpl(FacesContext context)
981   {
982     Iterator<UIComponent> kids = getFacetsAndChildren();
983     while (kids.hasNext())
984     {
985       UIComponent kid = kids.next();
986       kid.processDecodes(context);
987     }
988   }
989 
990 
991   /**
992    * Delegates to LifecycleRenderer, if present,
993    * otherwise calls validateChildrenImpl.
994    *
995    * @param context the current FacesContext
996    */
997   final protected void validateChildren(FacesContext context)
998   {
999     LifecycleRenderer renderer = getLifecycleRenderer(context);
1000     // if there is a ExtendedRenderer for this component
1001     if (renderer != null)
1002     {
1003       if (renderer.validateChildren(context, this))
1004         return;
1005     }
1006 
1007     validateChildrenImpl(context);
1008   }
1009 
1010   /**
1011    * Calls processValidators on all facets and children of this
1012    * component.
1013    * @param context the current FacesContext
1014    */
1015   protected void validateChildrenImpl(FacesContext context)
1016   {
1017     // Process all the facets and children of this component
1018     Iterator<UIComponent> kids = getFacetsAndChildren();
1019     while (kids.hasNext())
1020     {
1021       UIComponent kid = kids.next();
1022       kid.processValidators(context);
1023     }
1024   }
1025 
1026 
1027   /**
1028    * Delegates to LifecycleRenderer, if present,
1029    * otherwise calls upateChildrenImpl.
1030    *
1031    * @param context the current FacesContext
1032    */
1033   final protected void updateChildren(FacesContext context)
1034   {
1035     LifecycleRenderer renderer = getLifecycleRenderer(context);
1036     // if there is a ExtendedRenderer for this component
1037     if (renderer != null)
1038     {
1039       if (renderer.updateChildren(context, this))
1040         return;
1041     }
1042 
1043     updateChildrenImpl(context);
1044   }
1045 
1046   protected void updateChildrenImpl(FacesContext context)
1047   {
1048     // Process all the facets and children of this component
1049     Iterator<UIComponent> kids = getFacetsAndChildren();
1050     while (kids.hasNext())
1051     {
1052       UIComponent kid = kids.next();
1053       kid.processUpdates(context);
1054     }
1055   }
1056 
1057   @Override
1058   protected void addFacesListener(FacesListener listener)
1059   {
1060     if (listener == null)
1061       throw new NullPointerException();
1062 
1063     getFacesBean().addEntry(_LISTENERS_KEY, listener);
1064   }
1065 
1066   @Override
1067   protected void removeFacesListener(FacesListener listener)
1068   {
1069     if (listener == null)
1070       throw new NullPointerException();
1071 
1072     getFacesBean().removeEntry(_LISTENERS_KEY, listener);
1073   }
1074 
1075   @Override
1076   protected FacesListener[] getFacesListeners(Class clazz)
1077   {
1078     if (clazz == null)
1079       throw new NullPointerException();
1080 
1081     if (!FacesListener.class.isAssignableFrom(clazz))
1082       throw new IllegalArgumentException();
1083 
1084     return (FacesListener[])
1085        getFacesBean().getEntries(_LISTENERS_KEY, clazz);
1086   }
1087 
1088   protected void addAttributeChange(
1089     String attributeName,
1090     Object attributeValue)
1091   {
1092     AttributeComponentChange aa =
1093       new AttributeComponentChange(attributeName, attributeValue);
1094     RequestContext adfContext = RequestContext.getCurrentInstance();
1095     adfContext.getChangeManager().addComponentChange(getFacesContext(), this, aa);
1096   }
1097 
1098   void __rendererDecode(FacesContext context)
1099   {
1100     _cacheRenderer(context);
1101     Renderer renderer = getRenderer(context);
1102     // if there is a Renderer for this component
1103     if (renderer != null)
1104     {
1105       renderer.decode(context, this);
1106     }
1107   }
1108 
1109   private void _cacheRenderer(FacesContext context)
1110   {
1111     Renderer renderer = _getRendererImpl(context);
1112     _cachedRenderer = renderer;
1113 
1114     // cache the lifecycle renderer
1115     if (renderer instanceof LifecycleRenderer)
1116     {
1117       _cachedLifecycleRenderer = (LifecycleRenderer)renderer;
1118     }
1119     else
1120     {
1121       _cachedLifecycleRenderer = null;
1122     }
1123   }
1124 
1125   private Renderer _getRendererImpl(FacesContext context)
1126   {
1127     String rendererType = getRendererType();
1128     if (rendererType != null)
1129     {
1130       RenderKit renderKit = context.getRenderKit();
1131       Renderer renderer = renderKit.getRenderer(getFamily(), rendererType);
1132       if (renderer == null)
1133       {
1134         _LOG.warning("CANNOT_FIND_RENDERER", new Object[]{this, rendererType});
1135       }
1136 
1137       return renderer;
1138     }
1139 
1140     return null;
1141   }
1142 
1143   private LifecycleRenderer _getLifecycleRendererImpl(FacesContext context)
1144   {
1145     Renderer renderer = _getRendererImpl(context);
1146     if (renderer instanceof LifecycleRenderer)
1147     {
1148       return (LifecycleRenderer)renderer;
1149     }
1150 
1151     return null;
1152   }
1153 
1154   @Override
1155   protected Renderer getRenderer(FacesContext context)
1156   {
1157     Renderer renderer = _cachedRenderer;
1158     if (renderer != _UNDEFINED_RENDERER)
1159       return renderer;
1160 
1161     return _getRendererImpl(context);
1162   }
1163 
1164   protected LifecycleRenderer getLifecycleRenderer(FacesContext context)
1165   {
1166     LifecycleRenderer renderer = _cachedLifecycleRenderer;
1167     if (renderer != _UNDEFINED_LIFECYCLE_RENDERER)
1168       return renderer;
1169 
1170     return _getLifecycleRendererImpl(context);
1171 
1172   }
1173 
1174   protected void setProperty(PropertyKey key, Object value)
1175   {
1176     getFacesBean().setProperty(key, value);
1177   }
1178 
1179   protected Object getProperty(PropertyKey key)
1180   {
1181     return getFacesBean().getProperty(key);
1182   }
1183 
1184   protected void setBooleanProperty(PropertyKey key, boolean value)
1185   {
1186     getFacesBean().setProperty(key, value ? Boolean.TRUE : Boolean.FALSE);
1187   }
1188 
1189   protected boolean getBooleanProperty(PropertyKey key, boolean defaultValue)
1190   {
1191     Object o = getFacesBean().getProperty(key);
1192     if (defaultValue)
1193       return !Boolean.FALSE.equals(o);
1194     else
1195       return Boolean.TRUE.equals(o);
1196   }
1197 
1198   protected void setIntProperty(PropertyKey key, int value)
1199   {
1200     getFacesBean().setProperty(key, Integer.valueOf(value));
1201   }
1202 
1203   protected int getIntProperty(PropertyKey key, int defaultValue)
1204   {
1205     Number n = (Number) getFacesBean().getProperty(key);
1206     if (n == null)
1207       return defaultValue;
1208 
1209     return n.intValue();
1210   }
1211 
1212 
1213   /**
1214    * Return the number of facets.  This is more efficient than
1215    * calling getFacets().size();
1216    */
1217   @Override
1218   public int getFacetCount()
1219   {
1220     if (_facets == null)
1221       return 0;
1222 
1223     return _facets.size();
1224   }
1225 
1226 
1227   /**
1228    * Broadcast an event to a MethodBinding.
1229    * This can be used to support MethodBindings such as the "actionListener"
1230    * binding on ActionSource components:
1231    * &lt;tr:commandButton actionListener="#{mybean.myActionListener}">
1232    * @deprecated
1233    */
1234   protected final void broadcastToMethodBinding(
1235     FacesEvent event,
1236     MethodBinding method) throws AbortProcessingException
1237   {
1238     if (method != null)
1239     {
1240       try
1241       {
1242         FacesContext context = getFacesContext();
1243         method.invoke(context, new Object[] { event });
1244       }
1245       catch (EvaluationException ee)
1246       {
1247         Throwable t = ee.getCause();
1248         // Unwrap AbortProcessingExceptions
1249         if (t instanceof AbortProcessingException)
1250           throw ((AbortProcessingException) t);
1251         throw ee;
1252       }
1253     }
1254   }
1255 
1256   /**
1257    * Given a MethodBinding, create a MethodExpression that
1258    * adapts it.
1259    */
1260   static public MethodExpression adaptMethodBinding(MethodBinding binding)
1261   {
1262     return new MethodBindingMethodExpression(binding);
1263   }
1264 
1265   /**
1266    * Broadcast an event to a MethodExpression.
1267    * This can be used to support MethodBindings such as the "actionListener"
1268    * binding on ActionSource components:
1269    * &lt;tr:commandButton actionListener="#{mybean.myActionListener}">
1270    */
1271   protected final void broadcastToMethodExpression(
1272     FacesEvent event,
1273     MethodExpression method) throws AbortProcessingException
1274   {
1275     if (method != null)
1276     {
1277       try
1278       {
1279         FacesContext context = getFacesContext();
1280         method.invoke(context.getELContext(), new Object[] { event });
1281       }
1282       catch (ELException ee)
1283       {
1284         Throwable t = ee.getCause();
1285         // Unwrap AbortProcessingExceptions
1286         if (t instanceof AbortProcessingException)
1287           throw ((AbortProcessingException) t);
1288         throw ee;
1289       }
1290     }
1291   }
1292 
1293 
1294   /**
1295    * <p>
1296    * This gets a single threadlocal shared stringbuilder instance, each time you call
1297    * __getSharedStringBuilder it sets the length of the stringBuilder instance to 0.
1298    * </p><p>
1299    * This allows you to use the same StringBuilder instance over and over.
1300    * You must call toString on the instance before calling __getSharedStringBuilder again.
1301    * </p>
1302    * Example that works
1303    * <pre><code>
1304    * StringBuilder sb1 = __getSharedStringBuilder();
1305    * sb1.append(a).append(b);
1306    * String c = sb1.toString();
1307    *
1308    * StringBuilder sb2 = __getSharedStringBuilder();
1309    * sb2.append(b).append(a);
1310    * String d = sb2.toString();
1311    * </code></pre>
1312    * <br><br>
1313    * Example that doesn't work, you must call toString on sb1 before
1314    * calling __getSharedStringBuilder again.
1315    * <pre><code>
1316    * StringBuilder sb1 = __getSharedStringBuilder();
1317    * StringBuilder sb2 = __getSharedStringBuilder();
1318    *
1319    * sb1.append(a).append(b);
1320    * String c = sb1.toString();
1321    *
1322    * sb2.append(b).append(a);
1323    * String d = sb2.toString();
1324    * </code></pre>
1325    *
1326    */
1327   static StringBuilder __getSharedStringBuilder()
1328   {
1329     StringBuilder sb = _STRING_BUILDER.get();
1330 
1331     if (sb == null)
1332     {
1333       sb = new StringBuilder();
1334       _STRING_BUILDER.set(sb);
1335     }
1336 
1337     // clear out the stringBuilder by setting the length to 0
1338     sb.setLength(0);
1339 
1340     return sb;
1341   }
1342 
1343   /**
1344    * render a component. this is called by renderers whose
1345    * getRendersChildren() return true.
1346    */
1347   void __encodeRecursive(FacesContext context, UIComponent component)
1348     throws IOException
1349   {
1350     if (component.isRendered())
1351     {
1352       component.encodeBegin(context);
1353       if (component.getRendersChildren())
1354       {
1355         component.encodeChildren(context);
1356       }
1357       else
1358       {
1359         if (component.getChildCount() > 0)
1360         {
1361           for(UIComponent child : component.getChildren())
1362           {
1363             __encodeRecursive(context, child);
1364           }
1365         }
1366       }
1367 
1368       component.encodeEnd(context);
1369     }
1370   }
1371 
1372 
1373   static private UIComponent _findInsideOf(
1374     UIComponent from,
1375     String id)
1376   {
1377     Iterator<UIComponent> kids = from.getFacetsAndChildren();
1378     while (kids.hasNext())
1379     {
1380       UIComponent kid = kids.next();
1381       if (id.equals(kid.getId()))
1382         return kid;
1383 
1384       if (!(kid instanceof NamingContainer))
1385       {
1386         UIComponent returned = _findInsideOf(kid, id);
1387         if (returned != null)
1388           return returned;
1389       }
1390     }
1391 
1392     return null;
1393   }
1394 
1395   /**
1396    * <p>Verify that the specified component id is safe to add to the tree.
1397    * </p>
1398    *
1399    * @param id The proposed component id to check for validity
1400    *
1401    * @exception IllegalArgumentException if <code>id</code>
1402    *  is <code>null</code> or contains invalid characters
1403    */
1404   private void _validateId(String id)
1405   {
1406     if (id == null)
1407       return;
1408 
1409 
1410     int n = id.length();
1411     if (0 == n ||
1412         NamingContainer.SEPARATOR_CHAR == id.charAt(0))
1413       _throwBadId(id);
1414 
1415     for (int i = 0; i < n; i++)
1416     {
1417       char c = id.charAt(i);
1418       if (i == 0)
1419       {
1420         if (!Character.isLetter(c) && (c != '_'))
1421           _throwBadId(id);
1422       }
1423       else
1424       {
1425         if (!(Character.isLetter(c) ||
1426               Character.isDigit(c) ||
1427               (c == '-') || (c == '_')))
1428         {
1429           _throwBadId(id);
1430         }
1431       }
1432     }
1433   }
1434 
1435   private void _throwBadId(String id)
1436   {
1437     throw new IllegalArgumentException(_LOG.getMessage(
1438       "ILLEGAL_ID", id));
1439   }
1440 
1441   private void _init(
1442     String rendererType)
1443   {
1444     FacesBean oldBean = _facesBean;
1445     _facesBean = createFacesBean(rendererType);
1446     if (oldBean != null)
1447       _facesBean.addAll(oldBean);
1448 
1449     _attributes = new ValueMap(_facesBean);
1450   }
1451 
1452   private FacesBean                _facesBean;
1453   private List<UIComponent>        _children;
1454   private Map<String, Object>      _attributes;
1455   private Map<String, UIComponent> _facets;
1456   private UIComponent              _parent;
1457 
1458   // Cached instance of the Renderer for this component.
1459   // The instance will be re-retrieved in encodeBegin()
1460   private transient Renderer _cachedRenderer = _UNDEFINED_RENDERER;
1461   private transient LifecycleRenderer _cachedLifecycleRenderer =
1462                                                 _UNDEFINED_LIFECYCLE_RENDERER;
1463 
1464   // -= Simon Lessard =-
1465   // FIXME: _initialStateMarked is never read
1466   //        So commented out, is that ok? If so, this attribute should be deleted
1467   //private transient boolean _initialStateMarked;
1468 
1469   private static final Iterator<String> _EMPTY_STRING_ITERATOR =
1470     new EmptyIterator<String>();
1471 
1472   private static final Iterator<UIComponent> _EMPTY_UICOMPONENT_ITERATOR =
1473     new EmptyIterator<UIComponent>();
1474 
1475 
1476   static private final ThreadLocal<StringBuilder> _STRING_BUILDER =
1477                                                         new ThreadLocal<StringBuilder>();
1478 
1479   static private FacesBean.Type _createType()
1480   {
1481     try
1482     {
1483       ClassLoader cl = _getClassLoader();
1484       URL url = cl.getResource("META-INF/faces-bean-type.properties");
1485       if (url != null)
1486       {
1487         Properties properties = new Properties();
1488         InputStream is = url.openStream();
1489         try
1490         {
1491           properties.load(is);
1492           String className = (String)
1493             properties.get(UIXComponentBase.class.getName());
1494           return (FacesBean.Type) cl.loadClass(className).newInstance();
1495         }
1496         finally
1497         {
1498           is.close();
1499         }
1500       }
1501     }
1502     catch (Exception e)
1503     {
1504       _LOG.severe("CANNOT_LOAD_TYPE_PROPERTIES", e);
1505     }
1506 
1507     // For testing purposes, return a valid Type
1508     return new FacesBean.Type();
1509   }
1510 
1511   static private ClassLoader _getClassLoader()
1512   {
1513     ClassLoader loader = Thread.currentThread().getContextClassLoader();
1514     if (loader == null)
1515       loader = FacesBeanFactory.class.getClassLoader();
1516     return loader;
1517   }
1518 
1519   static private class RendererImpl extends Renderer
1520   {
1521   }
1522 
1523   static private class ExtendedRendererImpl extends ExtendedRenderer
1524   {
1525   }
1526 
1527   private static class EmptyIterator<T> implements Iterator<T>
1528   {
1529     public boolean hasNext()
1530     {
1531       return false;
1532     }
1533 
1534     public T next()
1535     {
1536       throw new NoSuchElementException();
1537     }
1538 
1539     public void remove()
1540     {
1541       throw new UnsupportedOperationException();
1542     }
1543 
1544   }
1545 
1546   static private final LifecycleRenderer _UNDEFINED_LIFECYCLE_RENDERER =
1547                                                 new ExtendedRendererImpl();
1548   static private final Renderer _UNDEFINED_RENDERER = new RendererImpl();
1549 }