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