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.IOException;
23  import java.io.InputStream;
24  import java.io.ObjectOutputStream;
25  
26  import java.net.URL;
27  
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Properties;
34  
35  import javax.el.ELContext;
36  import javax.el.ELException;
37  import javax.el.MethodExpression;
38  import javax.el.ValueExpression;
39  
40  import javax.faces.FacesException;
41  import javax.faces.application.ProjectStage;
42  import javax.faces.component.ContextCallback;
43  import javax.faces.component.NamingContainer;
44  import javax.faces.component.StateHolder;
45  import javax.faces.component.UIComponent;
46  import javax.faces.component.behavior.ClientBehavior;
47  import javax.faces.component.behavior.ClientBehaviorHolder;
48  import javax.faces.context.ExternalContext;
49  import javax.faces.context.FacesContext;
50  import javax.faces.el.EvaluationException;
51  import javax.faces.el.MethodBinding;
52  import javax.faces.el.ValueBinding;
53  import javax.faces.event.AbortProcessingException;
54  import javax.faces.event.ComponentSystemEvent;
55  import javax.faces.event.ComponentSystemEventListener;
56  import javax.faces.event.FacesEvent;
57  import javax.faces.event.FacesListener;
58  import javax.faces.event.PostAddToViewEvent;
59  import javax.faces.event.PreRemoveFromViewEvent;
60  import javax.faces.event.PreRenderComponentEvent;
61  import javax.faces.event.SystemEvent;
62  import javax.faces.event.SystemEventListener;
63  import javax.faces.render.RenderKit;
64  import javax.faces.render.Renderer;
65  
66  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
67  import org.apache.myfaces.trinidad.bean.AttachedObjects;
68  import org.apache.myfaces.trinidad.bean.FacesBean;
69  import org.apache.myfaces.trinidad.bean.FacesBeanFactory;
70  import org.apache.myfaces.trinidad.bean.PropertyKey;
71  import org.apache.myfaces.trinidad.bean.util.StateUtils;
72  import org.apache.myfaces.trinidad.bean.util.ValueMap;
73  import org.apache.myfaces.trinidad.change.AttributeComponentChange;
74  import org.apache.myfaces.trinidad.change.ComponentChange;
75  import org.apache.myfaces.trinidad.change.RowKeySetAttributeChange;
76  import org.apache.myfaces.trinidad.component.ComponentProcessingContext.ProcessingHint;
77  import org.apache.myfaces.trinidad.context.RenderingContext;
78  import org.apache.myfaces.trinidad.context.RequestContext;
79  import org.apache.myfaces.trinidad.event.AttributeChangeEvent;
80  import org.apache.myfaces.trinidad.event.AttributeChangeListener;
81  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
82  import org.apache.myfaces.trinidad.model.RowKeySet;
83  import org.apache.myfaces.trinidad.render.ExtendedRenderer;
84  import org.apache.myfaces.trinidad.render.LifecycleRenderer;
85  import org.apache.myfaces.trinidad.util.CollectionUtils;
86  import org.apache.myfaces.trinidad.util.ThreadLocalUtils;
87  
88  
89  /**
90   * Base implementation of components for all of Trinidad.  UIXComponentBase
91   * offers a number of features not supplied by the standard UIComponentBase
92   * class:
93   * <ul>
94   * <li>Use of FacesBean for better and easier state saving</li>
95   * <li>Support of the LifecycleRenderer class for greater Renderer
96   *  control over the lifecycle</li>
97   * <li>Built-in support for both the "partialTriggers" attribute
98   *   (declarative support for being a PPR target) and for triggering
99   *   such components (for being a the source of a PPR-causing event).</li>
100  * </ul>
101  * <h3>FacesBean and UIXComponentBase</h3>
102  * <p>
103  * UIXComponentBase differs from UIXComponent most particularly
104  * in its use of FacesBeans to store all state.  This offers
105  * a number of advantages:
106  * <ul>
107  * <li>Subclassers - if they use FacesBean for their state as well -
108  *   do not need to write overrides of saveState() and restoreState().
109  *   </li>
110  * <li>State is optimized by default</li>
111  * <li>Future optimizations - partly exposed today with
112  *    markInitialState() - can offer major state saving improvements.
113  * </ul>
114  * </p>
115  */
116 // TODO Write Class Javadoc
117 // TODO Thorough review against UIComponentBase
118 @JSFComponent
119 abstract public class UIXComponentBase extends UIXComponent
120 {
121   // Created up top to ensure it's present while we're processing
122   // class initialization code.
123   static private final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(UIXComponentBase.class);
124 
125   static public final FacesBean.Type TYPE = _createType();
126   static public final PropertyKey ID_KEY =
127     TYPE.registerKey("id", String.class, PropertyKey.CAP_NOT_BOUND);
128   static public final PropertyKey RENDERED_KEY =
129     TYPE.registerKey("rendered", Boolean.class, Boolean.TRUE);
130   static public final PropertyKey BINDING_KEY =
131     TYPE.registerKey("binding");
132   static public final PropertyKey TRANSIENT_KEY =
133     TYPE.registerKey("transient", Boolean.class,
134                      PropertyKey.CAP_NOT_BOUND |
135                      PropertyKey.CAP_TRANSIENT);
136   static public final PropertyKey RENDERER_TYPE_KEY =
137     TYPE.registerKey("rendererType", String.class, PropertyKey.CAP_NOT_BOUND);
138   static private final PropertyKey _LISTENERS_KEY =
139     TYPE.registerKey("listeners", FacesListener[].class, PropertyKey.CAP_LIST);
140   static private final PropertyKey _ATTRIBUTE_CHANGE_LISTENER_KEY =
141     TYPE.registerKey("attributeChangeListener", MethodExpression.class);
142   static private final PropertyKey _CLIENT_BEHAVIORS_KEY =
143     TYPE.registerKey("clientBehaviors", AttachedObjects.class,
144                      PropertyKey.CAP_NOT_BOUND|PropertyKey.CAP_PARTIAL_STATE_HOLDER|PropertyKey.CAP_STATE_HOLDER);
145   static private final PropertyKey _SYSTEM_EVENT_LISTENERS_KEY =
146     TYPE.registerKey("systemEventListeners", AttachedObjects.class,
147                      PropertyKey.CAP_NOT_BOUND|PropertyKey.CAP_PARTIAL_STATE_HOLDER|PropertyKey.CAP_STATE_HOLDER);
148   // =-=AEW "parent", "rendersChildren", "childCount", "children",
149   // "facets", "facetsAndChildren", "family" all are technically
150   // bean properties, but they aren't exposed here...
151 
152   static
153   {
154     // Register a couple of PropertyKeys against names that
155     // the RI's UIComponentTag implementation is shoving
156     // into all components.  This is purely an optimization, but
157     // a very useful one.
158     TYPE.registerKey("javax.faces.webapp.COMPONENT_IDS",
159                      List.class,
160                      PropertyKey.CAP_NOT_BOUND);
161     TYPE.registerKey("javax.faces.webapp.FACET_NAMES",
162                      List.class,
163                      PropertyKey.CAP_NOT_BOUND);
164     TYPE.lock();
165   }
166 
167   public UIXComponentBase()
168   {
169   }
170 
171   public UIXComponentBase(String rendererType)
172   {
173     setRendererType(rendererType);
174   }
175 
176   protected FacesBean createFacesBean(
177     String rendererType)
178   {
179     FacesBean bean = FacesBeanFactory.createFacesBean(getClass(),
180                                                       rendererType);
181     UIXFacesBean uixBean = (UIXFacesBean) bean;
182     uixBean.init(this, getBeanType());
183     return uixBean;
184   }
185 
186   protected PropertyKey getPropertyKey(String name)
187   {
188     PropertyKey key = getBeanType().findKey(name);
189     if (key == null)
190       key = PropertyKey.createPropertyKey(name);
191 
192     return key;
193   }
194 
195   protected FacesBean.Type getBeanType()
196   {
197     return TYPE;
198   }
199 
200   @Override
201   public FacesBean getFacesBean()
202   {
203     if (_facesBean == null)
204       _init(null);
205 
206     return _facesBean;
207   }
208 
209   @Override
210   public String getContainerClientId(FacesContext context, UIComponent child)
211   {
212     return getContainerClientId(context);
213   }
214 
215   @Override
216   public void addAttributeChangeListener(AttributeChangeListener acl)
217   {
218     addFacesListener(acl);
219   }
220 
221   @Override
222   public void removeAttributeChangeListener(AttributeChangeListener acl)
223   {
224     removeFacesListener(acl);
225   }
226 
227   @Override
228   public AttributeChangeListener[] getAttributeChangeListeners()
229   {
230     return (AttributeChangeListener[])
231       getFacesListeners(AttributeChangeListener.class);
232   }
233 
234   @Override
235   public void setAttributeChangeListener(MethodExpression mb)
236   {
237     setProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY, mb);
238   }
239 
240   @Deprecated
241   public void setAttributeChangeListener(MethodBinding mb)
242   {
243     setAttributeChangeListener(adaptMethodBinding(mb));
244   }
245 
246   @Override
247   public MethodExpression getAttributeChangeListener()
248   {
249     return (MethodExpression) getProperty(_ATTRIBUTE_CHANGE_LISTENER_KEY);
250   }
251 
252 
253   @Override
254   public ValueExpression getValueExpression(String name)
255   {
256     if (name == null)
257       throw new NullPointerException();
258 
259     PropertyKey key = getPropertyKey(name);
260 
261     // Support standard RI behavior where getValueBinding()
262     // doesn't complain about being asked for a ValueBinding -
263     // but continue supporting strict behavior at FacesBean layer.
264     if (!key.getSupportsBinding())
265       return null;
266 
267     return getFacesBean().getValueExpression(key);
268   }
269 
270   @Override
271   public void setValueExpression(String name,
272                                  ValueExpression expression)
273   {
274     if (name == null)
275       throw new NullPointerException();
276 
277     if ((expression != null) && expression.isLiteralText())
278     {
279       ELContext context =
280           FacesContext.getCurrentInstance().getELContext();
281       getAttributes().put(name, expression.getValue(context));
282     }
283     else
284     {
285       PropertyKey key = getPropertyKey(name);
286       getFacesBean().setValueExpression(key, expression);
287     }
288   }
289 
290   /**
291    */
292   @Override
293   public ValueBinding getValueBinding(String name)
294   {
295     if (name == null)
296       throw new NullPointerException();
297 
298     PropertyKey key = getPropertyKey(name);
299 
300     // Support standard RI behavior where getValueBinding()
301     // doesn't complain about being asked for a ValueBinding -
302     // but continue supporting strict behavior at FacesBean layer.
303     if (!key.getSupportsBinding())
304       return null;
305 
306     return getFacesBean().getValueBinding(key);
307   }
308 
309   @Override
310   public void setValueBinding(String name, ValueBinding binding)
311   {
312     if (name == null)
313       throw new NullPointerException();
314 
315     PropertyKey key = getPropertyKey(name);
316     getFacesBean().setValueBinding(key, binding);
317   }
318 
319   @Override
320   public Map<String, Object> getAttributes()
321   {
322     if (_attributes == null)
323       _init(null);
324 
325     return _attributes;
326   }
327 
328   @Override
329   protected Iterator<UIComponent> getRenderedFacetsAndChildren(
330     FacesContext facesContext)
331   {
332     _cacheRenderer(facesContext);
333     return super.getRenderedFacetsAndChildren(facesContext);
334   }
335 
336   /**
337    * Convenience method for implementors of {@link FlattenedComponent} to setup either the
338    * visiting context or the encoding context based on if the {@link ComponentProcessingContext}
339    * is processing for encoding or not.
340    * @param facesContext The faces context
341    * @param cpContext The component processing context passed to
342    *                  {@link FlattenedComponent#processFlattenedChildren}
343    */
344   protected void setupFlattenedContext(
345     FacesContext               facesContext,
346     ComponentProcessingContext cpContext
347     )
348   {
349     if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING))
350     {
351       setupEncodingContext(facesContext, RenderingContext.getCurrentInstance());
352     }
353     else
354     {
355       setupVisitingContext(facesContext);
356     }
357   }
358 
359   /**
360    * Convenience method for implementors of {@link FlattenedComponent} to setup either the
361    * visiting context or the encoding context based on if the {@link ComponentProcessingContext}
362    * is processing for encoding or not.
363    * @param facesContext The faces context
364    * @param cpContext The component processing context passed to
365    *                  {@link FlattenedComponent#processFlattenedChildren}
366    */
367   protected void setupFlattenedChildrenContext(
368     FacesContext               facesContext,
369     ComponentProcessingContext cpContext
370     )
371   {
372     if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING))
373     {
374       setupChildrenEncodingContext(facesContext, RenderingContext.getCurrentInstance());
375     }
376     else
377     {
378       setupChildrenVisitingContext(facesContext);
379     }
380   }
381 
382   /**
383    * Convenience method for implementors of {@link FlattenedComponent} to tear down either the
384    * visiting context or the encoding context based on if the {@link ComponentProcessingContext}
385    * is processing for encoding or not.
386    * @param facesContext The faces context
387    * @param cpContext The component processing context passed to
388    *                  {@link FlattenedComponent#processFlattenedChildren}
389    */
390   protected void tearDownFlattenedContext(
391     FacesContext               facesContext,
392     ComponentProcessingContext cpContext
393     )
394   {
395     if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING))
396     {
397       tearDownEncodingContext(facesContext, RenderingContext.getCurrentInstance());
398     }
399     else
400     {
401       tearDownVisitingContext(facesContext);
402     }
403   }
404 
405   /**
406    * Convenience method for implementors of {@link FlattenedComponent} to tear down either the
407    * visiting context or the encoding context based on if the {@link ComponentProcessingContext}
408    * is processing for encoding or not.
409    * @param facesContext The faces context
410    * @param cpContext The component processing context passed to
411    *                  {@link FlattenedComponent#processFlattenedChildren}
412    */
413   protected void tearDownFlattenedChildrenContext(
414     FacesContext               facesContext,
415     ComponentProcessingContext cpContext
416     )
417   {
418     if (cpContext.getHints().contains(ProcessingHint.PROCESS_FOR_ENCODING))
419     {
420       tearDownChildrenEncodingContext(facesContext, RenderingContext.getCurrentInstance());
421     }
422     else
423     {
424       tearDownChildrenVisitingContext(facesContext);
425     }
426   }
427 
428   // ------------------------------------------------------------- Properties
429 
430   /**
431    * Calculates the clientId for the component
432    */
433   private String _calculateClientId(FacesContext context)
434   {
435     // the clientId is always at least the id of the current component
436     // our implementation of getId() guarantees that this always
437     // returns a non-null value
438     String clientId = getId();
439 
440     // Search for an ancestor that is a naming container
441     UIComponent containerComponent = getParent();
442     while (null != containerComponent)
443     {
444       if (containerComponent instanceof NamingContainer)
445       {
446         String contClientId;
447 
448         // Pass additional context information to naming containers which extend UIXComponent:
449         if (containerComponent instanceof UIXComponent)
450           contClientId = ((UIXComponent)containerComponent).getContainerClientId(context, this);
451         else
452           contClientId = containerComponent.getContainerClientId(context);
453 
454         StringBuilder bld = __getSharedStringBuilder();
455         bld.append(contClientId).append(NamingContainer.SEPARATOR_CHAR).append(clientId);
456         clientId = bld.toString();
457         break;
458       }
459 
460       containerComponent = containerComponent.getParent();
461     }
462 
463     Renderer renderer = getRenderer(context);
464     if (null != renderer)
465       clientId = renderer.convertClientId(context, clientId);
466 
467     return clientId;
468   }
469 
470   @Override
471   public String getClientId(FacesContext context)
472   {
473     if (_isClientIdCachingEnabled())
474     {
475       String clientId = _clientId;
476 
477       if (clientId == null)
478       {
479         // This should not be called when the parent has not been set. Should it be
480         // called, the value will not be re-calculated correctly. This will be the case
481         // if someone attempts to invoke this function during facelets view construction.
482         if (_parent == null)
483         {
484           _LOG.warning("INVALID_CALL_TO_GETCLIENTID", getId());
485           // Return the value, even if not valid, for backward compatibility
486           return _calculateClientId(context);
487         }
488 
489         clientId = _calculateClientId(context);
490 
491         if (_usesFacesBeanImpl)
492         {
493           _clientId = clientId;
494         }
495       }
496       else if (_isClientIdDebuggingEnabled())
497       {
498         _warnClientIdCachingConfig(context);
499 
500         // for now validate success by checking the cached result against the dynamically
501         // generated result
502         String realID = _calculateClientId(context);
503 
504         if (!clientId.equals(realID))
505           throw new IllegalStateException(
506             String.format("Cached client id %s for %s doesn't match client id: %s",
507               clientId, this, realID));
508       }
509 
510       return clientId;
511     }
512     else
513     {
514       _warnClientIdCachingConfig(context);
515 
516       return _calculateClientId(context);
517     }
518   }
519 
520   /**
521    * Gets the identifier for the component.  This implementation
522    * never returns a null id.
523    */
524   @Override
525   public String getId()
526   {
527     // determine whether we can use the optimized code path or not
528     if (_usesFacesBeanImpl)
529     {
530       // optimized path
531 
532       // make sure that we always have an id
533       if (_id == null)
534       {
535         _id = FacesContext.getCurrentInstance().getViewRoot().createUniqueId();
536       }
537 
538       return _id;
539     }
540     else
541     {
542       // unoptimized path
543       FacesBean facesBean = getFacesBean();
544 
545       String id = (String)facesBean.getProperty(ID_KEY);
546 
547       // make sure that we always have an id
548       if (id == null)
549       {
550         id = FacesContext.getCurrentInstance().getViewRoot().createUniqueId();
551 
552         facesBean.setProperty(ID_KEY, id);
553       }
554 
555       return id;
556     }
557   }
558 
559   /**
560    * Sets the identifier for the component.  The identifier
561    * must follow a subset of the syntax allowed in HTML:
562    * <ul>
563    * <li>Must not be a zero-length String.</li>
564    * <li>First character must be an ASCII letter (A-Za-z) or an underscore ('_').</li>
565    * <li>Subsequent characters must be an ASCII letter or digit (A-Za-z0-9), an underscore ('_'), or a dash ('-').
566    * </ul>
567    */
568   @Override
569   public void setId(String id)
570   {
571     FacesBean facesBean = getFacesBean();
572 
573     // if we are using a FacesBeanImpl, then the FacesBean will
574     // delegate all calls to set the id back to us and we can store
575     // the value localy.  Otehrwise,w e need to store it in
576     // the FacesBean
577     if (_usesFacesBeanImpl)
578     {
579       // only validate if the id has actually changed
580       if ((_id == null) || !_id.equals(id))
581       {
582         _validateId(id);
583         _id = id;
584 
585         // if we're a NamingContainer then changing our id will invalidate the clientIds of all
586         // of our children
587         if ((_clientId != null) && (this instanceof NamingContainer))
588         {
589           clearCachedClientIds(this);
590         }
591       }
592     }
593     else
594     {
595       _validateId(id);
596       facesBean.setProperty(ID_KEY, id);
597     }
598 
599     _clientId = null;
600   }
601 
602   @Override
603   abstract public String getFamily();
604 
605   @Override
606   public UIComponent getParent()
607   {
608     return _parent;
609   }
610 
611   /**
612    * <p>Set the parent <code>UIComponent</code> of this
613    * <code>UIComponent</code>.</p>
614    *
615    * @param parent The new parent, or <code>null</code> for the root node
616    *  of a component tree
617    */
618   @Override
619   public void setParent(UIComponent parent)
620   {
621     // do we add this component ?
622     if (parent != _parent)
623     {
624       if (parent != null)
625       {
626         // set the reference
627         _parent = parent;
628         _resetClientId();
629 
630         if (parent.isInView())
631         {
632           // trigger the ADD_EVENT and call setInView(true)
633           // recursive for all kids/facets...
634           // Application.publishEvent(java.lang.Class, java.lang.Object)  must be called, passing
635           // PostAddToViewEvent.class as the first argument and the newly added component as the second
636           // argument.
637           _publishPostAddToViewEvent(getFacesContext(), this);
638         }
639       }
640       else
641       {
642         if (_parent != null && _parent.isInView())
643         {
644           // trigger the "remove event" lifecycle
645           // and call setInView(false) for all children/facets
646           // doing this => recursive
647           _publishPreRemoveFromViewEvent(getFacesContext(), this);
648         }
649 
650         // (un)set the reference
651         _parent = parent;
652         _resetClientId();
653       }
654     }
655   }
656 
657   private void _resetClientId()
658   {
659     // clear cached client ids if necessary
660     if (_clientId != null)
661     {
662       String newClientId = _calculateClientId(FacesContext.getCurrentInstance());
663 
664       // if our clientId changed as a result of being reparented (because we moved
665       // between NamingContainers for instance) then we need to clear out
666       // all of the cached client ids for our subtree
667       if (!_clientId.equals(newClientId))
668       {
669         clearCachedClientIds();
670         _clientId = newClientId;
671       }
672     }
673   }
674 
675   @Override
676   public boolean isRendered()
677   {
678     return getBooleanProperty(RENDERED_KEY, true);
679   }
680 
681   @Override
682   public void setRendered(boolean rendered)
683   {
684     setBooleanProperty(RENDERED_KEY, rendered);
685   }
686 
687   public boolean isTransient()
688   {
689     return getBooleanProperty(TRANSIENT_KEY, false);
690   }
691 
692   public void setTransient(boolean newTransient)
693   {
694     setBooleanProperty(TRANSIENT_KEY, newTransient);
695   }
696 
697   @Override
698   public String getRendererType()
699   {
700     // Don't create the FacesBean just to get the renderer type;
701     // Generally, rendererType will be the first property
702     // set, which will trigger the code below in setRendererType()
703     // to run correctly.
704     if (_facesBean == null)
705       return null;
706 
707     return (String) getProperty(RENDERER_TYPE_KEY);
708   }
709 
710   @Override
711   public void setRendererType(String rendererType)
712   {
713     String oldRendererType = getRendererType();
714     if (oldRendererType == null)
715     {
716       if (rendererType == null)
717         return;
718     }
719     else if (oldRendererType.equals(rendererType))
720     {
721       return;
722     }
723 
724     // prepare the faces bean
725     _init(rendererType);
726     setProperty(RENDERER_TYPE_KEY, rendererType);
727   }
728 
729   @Override
730   public boolean getRendersChildren()
731   {
732     Renderer renderer = getRenderer(getFacesContext());
733     if (renderer == null)
734       return false;
735 
736     return renderer.getRendersChildren();
737   }
738 
739   // ------------------------------------------------ Tree Management Methods
740 
741   @Override
742   public UIComponent findComponent(String id)
743   {
744     if (id == null)
745       throw new NullPointerException();
746 
747     if ("".equals(id))
748       throw new IllegalArgumentException();
749 
750     UIComponent from = this;
751     // If it starts with the separator character, it's
752     // an absolute path: start at the top
753     if (id.charAt(0) == NamingContainer.SEPARATOR_CHAR)
754     {
755       id = id.substring(1);
756       while (from.getParent() != null)
757         from = from.getParent();
758     }
759     // If it's a NamingContainer, start right here
760     else if (this instanceof NamingContainer)
761     {
762       ;
763     }
764     // Otherwise, go up to look for NamingContainer (or the top)
765     else
766     {
767       while (from.getParent() != null)
768       {
769         from = from.getParent();
770         if (from instanceof NamingContainer)
771           break;
772       }
773     }
774 
775     // Evaluate each part of the expression
776     String searchId;
777     int separatorIndex = id.indexOf(NamingContainer.SEPARATOR_CHAR);
778     if (separatorIndex < 0)
779       searchId = id;
780     else
781       searchId = id.substring(0, separatorIndex);
782 
783     if (searchId.equals(from.getId()))
784     {
785       // Don't need to look inside if we're already there
786       ;
787     }
788     else
789     {
790       from = _findInsideOf(from, searchId);
791     }
792 
793     // Final ID:  break, and return whatever we've found
794     if (separatorIndex < 0)
795     {
796       return from;
797     }
798     // Just an intermediate step.  Make sure we're at a NamingContainer,
799     // and then ask it to find the rest of the expression.
800     else
801     {
802       if (from == null)
803         return null;
804 
805       if (!(from instanceof NamingContainer))
806         throw new IllegalArgumentException();
807       return from.findComponent(id.substring(separatorIndex + 1));
808     }
809   }
810 
811   /**
812    * <p>Create (if necessary) and return a List of the children associated
813    * with this component.</p>
814    */
815   @Override
816   public List<UIComponent> getChildren()
817   {
818     if (_children == null)
819       _children = new ChildArrayList(this);
820 
821     return _children;
822   }
823 
824   @Override
825   public int getChildCount()
826   {
827     if (_children == null)
828       return 0;
829     else
830       return getChildren().size();
831   }
832 
833   /**
834    * <p>Create (if necessary) and return a Map of the facets associated
835    * with this component.</p>
836    */
837   @Override
838   public Map<String, UIComponent> getFacets()
839   {
840 
841     if (_facets == null)
842       _facets = new FacetHashMap(this);
843 
844     return _facets;
845   }
846 
847   @Override
848   public UIComponent getFacet(String facetName)
849   {
850     if (facetName == null)
851       throw new NullPointerException();
852 
853     if (_facets == null)
854       return null;
855     else
856       return getFacets().get(facetName);
857   }
858 
859   /**
860    * Returns an Iterator over the names of all facets.
861    * Unlike getFacets().keySet().iterator(), this does
862    * not require instantiating a Map if there are
863    * no facets.  (Note that this is not part of the
864    * UIComponent API.)
865    */
866   public Iterator<String> getFacetNames()
867   {
868     if (_facets == null)
869       return _EMPTY_STRING_ITERATOR;
870     else
871       return _facets.keySet().iterator();
872   }
873 
874   @Override
875   public Iterator<UIComponent> getFacetsAndChildren()
876   {
877     // =-=AEW Is this supposed to be an immutable Iterator?
878     if (_facets == null)
879     {
880       if (_children == null)
881         return _EMPTY_UICOMPONENT_ITERATOR;
882 
883       return _children.iterator();
884     }
885     else
886     {
887       if (_children == null)
888         return _facets.values().iterator();
889     }
890 
891     return new CompositeIterator<UIComponent>(_children.iterator(), _facets.values().iterator());
892   }
893 
894   // ------------------------------------------- Event processing methods
895 
896   @Override
897   public void broadcast(FacesEvent event)
898     throws AbortProcessingException
899   {
900     if (event == null)
901       throw new NullPointerException();
902 
903     if (_LOG.isFine())
904       _LOG.fine("Broadcasting event " + event + " to " + this);
905 
906     UIComponent component = event.getComponent();
907     if (component != null && satisfiesPartialTrigger(event))
908     {
909       RequestContext adfContext = RequestContext.getCurrentInstance();
910       if (adfContext != null)
911         adfContext.partialUpdateNotify(component);
912     }
913 
914     Iterator<FacesListener> iter =
915       (Iterator<FacesListener>)getFacesBean().entries(_LISTENERS_KEY);
916 
917     while (iter.hasNext())
918     {
919       FacesListener listener = iter.next();
920       if (event.isAppropriateListener(listener))
921       {
922         event.processListener(listener);
923       }
924     }
925 
926     if (event instanceof AttributeChangeEvent)
927     {
928       broadcastToMethodExpression(event, getAttributeChangeListener());
929     }
930   }
931 
932   /**
933    * Check if a faces event broadcast to this component should trigger the partial updates of the
934    * target listeners of this component. By default, all events trigger a partial update of the listeners.
935    *
936    * @param event The event to check
937    * @return true if the partial triggers should be updated by this event being broadcast
938    */
939   protected boolean satisfiesPartialTrigger(
940     FacesEvent event)
941   {
942     return true;
943   }
944 
945   // ------------------------------------------- Lifecycle Processing Methods
946 
947   @Override
948   public void decode(FacesContext context)
949   {
950     if (context == null)
951       throw new NullPointerException();
952 
953     // Find all the partialTriggers and save on the context
954     Map<String, Object> attrs = getAttributes();
955     Object triggers = attrs.get("partialTriggers");
956     if (triggers instanceof String[])
957     {
958       RequestContext adfContext = RequestContext.getCurrentInstance();
959       if (adfContext != null)
960         adfContext.addPartialTriggerListeners(this, (String[]) triggers);
961     }
962 
963     __rendererDecode(context);
964   }
965 
966   @Override
967   public void encodeBegin(FacesContext context) throws IOException
968   {
969     if (context == null)
970       throw new NullPointerException();
971 
972     // Call UIComponent.pushComponentToEL(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
973     pushComponentToEL(context, this);
974 
975     if (!isRendered())
976       return;
977 
978     context.getApplication().publishEvent(context,  PreRenderComponentEvent.class, UIComponent.class, this);
979 
980     _cacheRenderer(context);
981     Renderer renderer = getRenderer(context);
982 
983     // if there is a Renderer for this component
984     if (renderer != null)
985     {
986       renderer.encodeBegin(context, this);
987     }
988   }
989 
990   @Override
991   public void encodeChildren(FacesContext context) throws IOException
992   {
993     if (context == null)
994       throw new NullPointerException();
995 
996     if (!isRendered())
997       return;
998 
999     Renderer renderer = getRenderer(context);
1000     // if there is a Renderer for this component
1001     if (renderer != null)
1002     {
1003       renderer.encodeChildren(context, this);
1004     }
1005   }
1006 
1007   @Override
1008   public void encodeEnd(FacesContext context) throws IOException
1009   {
1010     if (context == null)
1011       throw new NullPointerException();
1012 
1013     try
1014     {
1015       if (isRendered())
1016       {
1017         Renderer renderer = getRenderer(context);
1018         // if there is a Renderer for this component
1019         if (renderer != null)
1020         {
1021           renderer.encodeEnd(context, this);
1022         }
1023       }
1024     }
1025     finally
1026     {
1027       popComponentFromEL(context);
1028     }
1029   }
1030 
1031   @Override
1032   public void queueEvent(FacesEvent event)
1033   {
1034     if (event == null)
1035       throw new NullPointerException();
1036 
1037     UIComponent parent = getParent();
1038     if (parent == null)
1039       throw new IllegalStateException();
1040 
1041     parent.queueEvent(event);
1042   }
1043 
1044   // ----------------------------------------------- Lifecycle Phase Handlers
1045 
1046   @Override
1047   public void processDecodes(FacesContext context)
1048   {
1049     if (context == null)
1050       throw new NullPointerException();
1051 
1052     if (!isRendered())
1053       return;
1054 
1055     pushComponentToEL(context, this);
1056 
1057     try
1058     {
1059       // Process all facets and children of this component
1060       decodeChildren(context);
1061 
1062       // Process this component itself
1063       decode(context);
1064     }
1065     finally
1066     {
1067       // Call UIComponent.popComponentFromEL(javax.faces.context.FacesContext) from inside of a finally
1068       // block, just before returning.
1069 
1070       popComponentFromEL(context);
1071     }
1072   }
1073 
1074   @Override
1075   public void processValidators(FacesContext context)
1076   {
1077     if (context == null)
1078       throw new NullPointerException();
1079 
1080     if (!isRendered())
1081       return;
1082 
1083     pushComponentToEL(context, this);
1084 
1085     try
1086     {
1087       // Process all facets and children of this component
1088       validateChildren(context);
1089     }
1090     finally
1091     {
1092       popComponentFromEL(context);
1093     }
1094   }
1095 
1096   @Override
1097   public void processUpdates(FacesContext context)
1098   {
1099     if (context == null)
1100       throw new NullPointerException();
1101 
1102     if (!isRendered())
1103       return;
1104 
1105     pushComponentToEL(context, this);
1106 
1107     try
1108     {
1109       // Process all facets and children of this component
1110       updateChildren(context);
1111     }
1112     finally
1113     {
1114       popComponentFromEL(context);
1115     }
1116   }
1117 
1118   @Override
1119   public Object processSaveState(FacesContext context)
1120   {
1121     if (context == null)
1122       throw new NullPointerException();
1123 
1124     if (_LOG.isFiner())
1125       _LOG.finer("processSaveState() on " + this);
1126 
1127     pushComponentToEL(context, this);
1128 
1129     Object state = null;
1130 
1131     try
1132     {
1133       if (((_children == null) || _children.isEmpty()) &&
1134           ((_facets == null) || _facets.isEmpty()))
1135       {
1136         state = saveState(context);
1137       }
1138       else
1139       {
1140         TreeState treeState = new TreeState();
1141         treeState.saveState(context, this);
1142         if (treeState.isEmpty())
1143           state = null;
1144 
1145         state = treeState;
1146       }
1147     }
1148     catch (RuntimeException e)
1149     {
1150       _LOG.warning(_LOG.getMessage("COMPONENT_CHILDREN_SAVED_STATE_FAILED", this));
1151 
1152       throw e;
1153     }
1154 
1155     finally
1156     {
1157       popComponentFromEL(context);
1158     }
1159 
1160     return state;
1161   }
1162 
1163   // TODO  will have deep problems if UIComponent.saveState() ever
1164   //   returns a String.
1165   // TODO crashes and burns if there are fewer children or missing
1166   //  facets from when state was saved.
1167   @Override
1168   public void processRestoreState(FacesContext context, Object state)
1169   {
1170     if (context == null)
1171       throw new NullPointerException();
1172 
1173     if (_LOG.isFiner())
1174       _LOG.finer("processRestoreState() on " + this);
1175 
1176     pushComponentToEL(context, this);
1177 
1178     try
1179     {
1180       // If we saved a "TreeState", use it to restore everything
1181       if (state instanceof TreeState)
1182       {
1183         ((TreeState) state).restoreState(context, this);
1184       }
1185       // Otherwise, we had no children or facets, and just use
1186       // the "state" object
1187       else
1188       {
1189         restoreState(context, state);
1190       }
1191     }
1192     finally
1193     {
1194       popComponentFromEL(context);
1195     }
1196   }
1197 
1198   @Override
1199   public void markInitialState()
1200   {
1201     // -= Simon Lessard =-
1202     // FIXME: Set to true, but never read
1203     //_initialStateMarked = true;
1204     getFacesBean().markInitialState();
1205   }
1206 
1207   @Override
1208   public void clearInitialState()
1209   {
1210     getFacesBean().clearInitialState();
1211   }
1212 
1213   @Override
1214   public boolean initialStateMarked()
1215   {
1216     return getFacesBean().initialStateMarked();
1217   }
1218 
1219   public Object saveState(FacesContext facesContext)
1220   {
1221     Object state = getFacesBean().saveState(facesContext);
1222 
1223     // if component state serialization checking is on, attempt to Serialize the
1224     // component state immediately in order to determine which component's state
1225     // failed state saving.
1226     if (StateUtils.checkComponentStateSerialization(facesContext))
1227     {
1228       try
1229       {
1230         new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(state);
1231       }
1232       catch (IOException e)
1233       {
1234         throw new RuntimeException(_LOG.getMessage("COMPONENT_SAVED_STATE_FAILED", this), e);
1235       }
1236     }
1237 
1238     return state;
1239   }
1240 
1241   public void restoreState(
1242     FacesContext facesContext,
1243     Object       stateObj)
1244   {
1245     getFacesBean().restoreState(facesContext, stateObj);
1246   }
1247 
1248   @Override
1249   public String toString()
1250   {
1251     String className = getClass().getName();
1252     int periodIndex = className.lastIndexOf('.');
1253     if (periodIndex >= 0)
1254       className = className.substring(periodIndex + 1);
1255 
1256     return className + "[" + getFacesBean().toString() + ", id=" + getId() + "]";
1257   }
1258 
1259   /**
1260    * <p>Return the {@link FacesContext} instance for the current request.</p>
1261    */
1262   @Override
1263   protected FacesContext getFacesContext()
1264   {
1265     // If we ever have a way for a component to get notified
1266     // when it's finished being used for a given request,
1267     // we could cache this as an instance variable.
1268     return FacesContext.getCurrentInstance();
1269   }
1270 
1271   /**
1272    * Delegates to LifecycleRenderer, if present,
1273    * otherwise calls decodeChildrenImpl.
1274    *
1275    * @param context the current FacesContext
1276    */
1277   final protected void decodeChildren(FacesContext context)
1278   {
1279     LifecycleRenderer renderer = getLifecycleRenderer(context);
1280     // if there is a HierarchyRenderer for this component
1281     if (renderer != null)
1282     {
1283       if (renderer.decodeChildren(context, this))
1284         return;
1285     }
1286 
1287     decodeChildrenImpl(context);
1288   }
1289 
1290   /**
1291    * Calls processDecodes on all facets and children of this
1292    * component.
1293    * @param context the current FacesContext
1294    */
1295   protected void decodeChildrenImpl(FacesContext context)
1296   {
1297     Iterator<UIComponent> kids = getRenderedFacetsAndChildren(context);
1298     while (kids.hasNext())
1299     {
1300       UIComponent kid = kids.next();
1301       kid.processDecodes(context);
1302     }
1303   }
1304 
1305   /**
1306    * Delegates to LifecycleRenderer, if present,
1307    * otherwise calls validateChildrenImpl.
1308    *
1309    * @param context the current FacesContext
1310    */
1311   final protected void validateChildren(FacesContext context)
1312   {
1313     LifecycleRenderer renderer = getLifecycleRenderer(context);
1314     // if there is a ExtendedRenderer for this component
1315     if (renderer != null)
1316     {
1317       if (renderer.validateChildren(context, this))
1318         return;
1319     }
1320 
1321     validateChildrenImpl(context);
1322   }
1323 
1324   /**
1325    * Calls processValidators on all facets and children of this
1326    * component.
1327    * @param context the current FacesContext
1328    */
1329   protected void validateChildrenImpl(FacesContext context)
1330   {
1331     // Process all the facets and children of this component
1332     Iterator<UIComponent> kids = getRenderedFacetsAndChildren(context);
1333     while (kids.hasNext())
1334     {
1335       UIComponent kid = kids.next();
1336       kid.processValidators(context);
1337     }
1338   }
1339 
1340   /**
1341    * Delegates to LifecycleRenderer, if present,
1342    * otherwise calls upateChildrenImpl.
1343    *
1344    * @param context the current FacesContext
1345    */
1346   final protected void updateChildren(FacesContext context)
1347   {
1348     LifecycleRenderer renderer = getLifecycleRenderer(context);
1349     // if there is a ExtendedRenderer for this component
1350     if (renderer != null)
1351     {
1352       if (renderer.updateChildren(context, this))
1353         return;
1354     }
1355 
1356     updateChildrenImpl(context);
1357   }
1358 
1359   protected void updateChildrenImpl(FacesContext context)
1360   {
1361     // Process all the facets and children of this component
1362     Iterator<UIComponent> kids = getRenderedFacetsAndChildren(context);
1363     while (kids.hasNext())
1364     {
1365       UIComponent kid = kids.next();
1366       kid.processUpdates(context);
1367     }
1368   }
1369 
1370   @Override
1371   protected void addFacesListener(FacesListener listener)
1372   {
1373     if (listener == null)
1374       throw new NullPointerException();
1375 
1376     getFacesBean().addEntry(_LISTENERS_KEY, listener);
1377   }
1378 
1379   @Override
1380   protected void removeFacesListener(FacesListener listener)
1381   {
1382     if (listener == null)
1383       throw new NullPointerException();
1384 
1385     getFacesBean().removeEntry(_LISTENERS_KEY, listener);
1386   }
1387 
1388   @Override
1389   protected FacesListener[] getFacesListeners(Class clazz)
1390   {
1391     if (clazz == null)
1392       throw new NullPointerException();
1393 
1394     if (!FacesListener.class.isAssignableFrom(clazz))
1395       throw new IllegalArgumentException();
1396 
1397     return (FacesListener[])
1398        getFacesBean().getEntries(_LISTENERS_KEY, clazz);
1399   }
1400 
1401   /**
1402    * Adds a change for a Component, or the Component's subtree, returning the change actually added,
1403    * or <code>null</code>, if no change was added.  The proposed change may be rejected by the
1404    * component itself, one of its ancestors, or the ChangeManager implementation.
1405    * @param change     The change to add for this component
1406    * @return The ComponentChange actually added, or
1407    * <code>null</code> if no change was added.
1408    * @see #addComponentChange(UIComponent, ComponentChange)
1409    */
1410   public final ComponentChange addComponentChange(ComponentChange change)
1411   {
1412     return addComponentChange(this, change);
1413   }
1414   
1415   private UIXComponentBase _getNextUIXComponentBaseAnxcestor()
1416   {
1417     UIComponent parent = getParent();
1418 
1419     while (parent != null)
1420     {    
1421       if (parent instanceof UIXComponentBase)
1422       {
1423         return (UIXComponentBase)parent;
1424       }
1425       
1426       parent = parent.getParent();
1427     }
1428     
1429     return null;
1430   }
1431 
1432   /**
1433    * Called when adding a change to a Component, or the Component's subtree.
1434    * The default implementation delegates the call to the parent, if possible, otherwise
1435    * it adds the change to the ChangeManager directly.
1436    * Subclasses can override this method to among other things, filter or transform the changes.
1437    * @param component  The component that the change is for
1438    * @param change     The change to add for this component
1439    * @return The ComponentChange actually added, or
1440    * <code>null</code> if no change was added.
1441    * @see #addComponentChange(ComponentChange)
1442    * @see #addAttributeChange
1443    */
1444   protected ComponentChange addComponentChange(UIComponent component, ComponentChange change)
1445   {
1446     // check moved from addAttributeChange(), as this is more central
1447     if ((component == this) && (change instanceof AttributeComponentChange))
1448     {
1449       AttributeComponentChange aa             = (AttributeComponentChange)change;
1450       Object                   attributeValue = aa.getAttributeValue();
1451       
1452       if (attributeValue instanceof RowKeySet)
1453       {
1454         change = new RowKeySetAttributeChange(getClientId(getFacesContext()),
1455                                               aa.getAttributeName(),
1456                                               attributeValue);
1457       }
1458     }
1459     
1460     // add the change unless changes for this subtree have suppressed
1461     if (!Boolean.TRUE.equals(getAttributes().get(_NO_SUBTREE_CHANGE_ATTR)))
1462     {
1463       UIXComponentBase nextUIXParent = _getNextUIXComponentBaseAnxcestor();
1464   
1465       if (nextUIXParent != null)
1466       {
1467         return nextUIXParent.addComponentChange(component, change);
1468       }
1469       else
1470       {
1471         RequestContext trinContext = RequestContext.getCurrentInstance();
1472         trinContext.getChangeManager().addComponentChange(getFacesContext(), component, change);
1473         return change;
1474       }
1475     }
1476     else
1477     {
1478       return null;
1479     }
1480   }
1481 
1482 
1483   /**
1484    * Convenience function for
1485    * <code>addComponentChange(new AttributeComponentChange(attributeName, attributeValue));</code>
1486    * This function is not <code>final</code> for backwards compatibility reasons, however,
1487    * existing subclassers whould override <code>addComponentChange</code> instead.
1488    * @param attributeName
1489    * @param attributeValue
1490    * @see #addComponentChange(UIComponent, ComponentChange)
1491    */
1492   protected void addAttributeChange(
1493     String attributeName,
1494     Object attributeValue)
1495   {
1496     addComponentChange(new AttributeComponentChange(attributeName, attributeValue));
1497   }
1498 
1499   void __rendererDecode(FacesContext context)
1500   {
1501     _cacheRenderer(context);
1502     Renderer renderer = getRenderer(context);
1503     // if there is a Renderer for this component
1504     if (renderer != null)
1505     {
1506       renderer.decode(context, this);
1507     }
1508   }
1509 
1510   /**
1511    * Publish PostAddToViewEvent to the component and all facets and children.
1512    *
1513    * @param context the current FacesContext
1514    * @param component the current UIComponent
1515    */
1516   private void _publishPostAddToViewEvent(
1517     FacesContext context,
1518     UIComponent component)
1519   {
1520     component.setInView(true);
1521     context.getApplication().publishEvent(context, PostAddToViewEvent.class, UIComponent.class, component);
1522 
1523     if (component.getChildCount() > 0)
1524     {
1525       List<UIComponent> children = component.getChildren();
1526       UIComponent child = null;
1527       UIComponent currentChild = null;
1528       int i = 0;
1529       while (i < children.size())
1530       {
1531         child = children.get(i);
1532 
1533         // Iterate over the same index if the component was removed
1534         // This prevents skip components when processing
1535         do
1536         {
1537           _publishPostAddToViewEvent(context, child);
1538           currentChild = child;
1539         }
1540         while ((i < children.size()) &&
1541                ((child = children.get(i)) != currentChild) );
1542         i++;
1543       }
1544     }
1545 
1546     if (component.getFacetCount() > 0)
1547     {
1548       for (UIComponent child : component.getFacets().values())
1549       {
1550         _publishPostAddToViewEvent(context, child);
1551       }
1552     }
1553   }
1554 
1555   /**
1556    * Publish PreRemoveFromViewEvent to the component and all facets and children.
1557    *
1558    * @param context the current FacesContext
1559    * @param component the current UIComponent
1560    */
1561   private void _publishPreRemoveFromViewEvent(
1562     FacesContext context,
1563     UIComponent component)
1564   {
1565     component.setInView(false);
1566     context.getApplication().publishEvent(context, PreRemoveFromViewEvent.class, UIComponent.class, component);
1567 
1568     if (component.getChildCount() > 0)
1569     {
1570       for (UIComponent child : component.getChildren())
1571       {
1572         _publishPreRemoveFromViewEvent(context, child);
1573       }
1574     }
1575 
1576     if (component.getFacetCount() > 0)
1577     {
1578       for (UIComponent child : component.getFacets().values())
1579       {
1580         _publishPreRemoveFromViewEvent(context, child);
1581       }
1582     }
1583   }
1584 
1585   private void _cacheRenderer(FacesContext context)
1586   {
1587     Renderer renderer = _getRendererImpl(context);
1588     _cachedRenderer = renderer;
1589 
1590     // cache the lifecycle renderer
1591     if (renderer instanceof LifecycleRenderer)
1592     {
1593       _cachedLifecycleRenderer = (LifecycleRenderer)renderer;
1594     }
1595     else
1596     {
1597       _cachedLifecycleRenderer = null;
1598     }
1599   }
1600 
1601   private Renderer _getRendererImpl(FacesContext context)
1602   {
1603     String rendererType = getRendererType();
1604     if (rendererType != null)
1605     {
1606       RenderKit renderKit = context.getRenderKit();
1607       Renderer renderer = renderKit.getRenderer(getFamily(), rendererType);
1608       if (renderer == null)
1609       {
1610         _LOG.warning("CANNOT_FIND_RENDERER", new Object[]{this, rendererType});
1611       }
1612 
1613       return renderer;
1614     }
1615 
1616     return null;
1617   }
1618 
1619   private LifecycleRenderer _getLifecycleRendererImpl(FacesContext context)
1620   {
1621     Renderer renderer = _getRendererImpl(context);
1622     if (renderer instanceof LifecycleRenderer)
1623     {
1624       return (LifecycleRenderer)renderer;
1625     }
1626 
1627     return null;
1628   }
1629 
1630   @Override
1631   protected Renderer getRenderer(FacesContext context)
1632   {
1633     Renderer renderer = _cachedRenderer;
1634     if (renderer != _UNDEFINED_RENDERER)
1635       return renderer;
1636 
1637     return _getRendererImpl(context);
1638   }
1639 
1640   protected LifecycleRenderer getLifecycleRenderer(FacesContext context)
1641   {
1642     LifecycleRenderer renderer = _cachedLifecycleRenderer;
1643     if (renderer != _UNDEFINED_LIFECYCLE_RENDERER)
1644       return renderer;
1645 
1646     return _getLifecycleRendererImpl(context);
1647 
1648   }
1649 
1650   protected void setProperty(PropertyKey key, Object value)
1651   {
1652     getFacesBean().setProperty(key, value);
1653   }
1654 
1655   protected Object getProperty(PropertyKey key)
1656   {
1657     return getFacesBean().getProperty(key);
1658   }
1659 
1660   protected void setBooleanProperty(PropertyKey key, boolean value)
1661   {
1662     getFacesBean().setProperty(key, value ? Boolean.TRUE : Boolean.FALSE);
1663   }
1664 
1665   protected boolean getBooleanProperty(PropertyKey key, boolean defaultValue)
1666   {
1667     Object o = getFacesBean().getProperty(key);
1668     if (defaultValue)
1669       return !Boolean.FALSE.equals(o);
1670     else
1671       return Boolean.TRUE.equals(o);
1672   }
1673 
1674   protected void setIntProperty(PropertyKey key, int value)
1675   {
1676     getFacesBean().setProperty(key, Integer.valueOf(value));
1677   }
1678 
1679   protected int getIntProperty(PropertyKey key, int defaultValue)
1680   {
1681     Number n = (Number) getFacesBean().getProperty(key);
1682     if (n == null)
1683       return defaultValue;
1684 
1685     return n.intValue();
1686   }
1687 
1688   /**
1689    * Return the number of facets.  This is more efficient than
1690    * calling getFacets().size();
1691    */
1692   @Override
1693   public int getFacetCount()
1694   {
1695     if (_facets == null)
1696       return 0;
1697 
1698     return _facets.size();
1699   }
1700 
1701   /**
1702    * Broadcast an event to a MethodBinding.
1703    * This can be used to support MethodBindings such as the "actionListener"
1704    * binding on ActionSource components:
1705    * &lt;tr:commandButton actionListener="#{mybean.myActionListener}">
1706    * @deprecated
1707    */
1708   protected final void broadcastToMethodBinding(
1709     FacesEvent event,
1710     MethodBinding method) throws AbortProcessingException
1711   {
1712     if (method != null)
1713     {
1714       try
1715       {
1716         FacesContext context = getFacesContext();
1717         method.invoke(context, new Object[] { event });
1718       }
1719       catch (EvaluationException ee)
1720       {
1721         // Checking for AbortProcessingExceptions, and unwrapping
1722         // it if the underlying exception is AbortProcessingExceptions.
1723         Throwable currentThrowable = ee.getCause();
1724         while (currentThrowable != null)
1725         {
1726           if (currentThrowable instanceof AbortProcessingException)
1727           {
1728             throw ((AbortProcessingException)currentThrowable);
1729           }
1730           currentThrowable = currentThrowable.getCause();
1731         }
1732         throw ee;
1733       }
1734     }
1735   }
1736 
1737   /**
1738    * Given a MethodBinding, create a MethodExpression that
1739    * adapts it.
1740    */
1741   static public MethodExpression adaptMethodBinding(MethodBinding binding)
1742   {
1743     return new MethodBindingMethodExpression(binding);
1744   }
1745 
1746   /**
1747    * Broadcast an event to a MethodExpression.
1748    * This can be used to support MethodBindings such as the "actionListener"
1749    * binding on ActionSource components:
1750    * &lt;tr:commandButton actionListener="#{mybean.myActionListener}">
1751    */
1752   protected final void broadcastToMethodExpression(
1753     FacesEvent event,
1754     MethodExpression method) throws AbortProcessingException
1755   {
1756     if (method != null)
1757     {
1758       try
1759       {
1760         FacesContext context = getFacesContext();
1761         method.invoke(context.getELContext(), new Object[] { event });
1762       }
1763       catch (ELException ee)
1764       {
1765         Throwable t = ee.getCause();
1766         // Unwrap AbortProcessingExceptions
1767         if (t instanceof AbortProcessingException)
1768           throw ((AbortProcessingException) t);
1769         throw ee;
1770       }
1771     }
1772   }
1773 
1774   /**
1775    * Convenience method to call <code>invokeOnComponent</code> on all of the
1776    * children of a component, surrounding the invocation with calls to
1777    * <code>setup/tearDownChildrenVisitingContext</code>.
1778    * This is useful when a component sometimes optimizes
1779    * away calling <code>invokeOnComponent</code> on its children.
1780    * @see UIXComponent#setupChildrenVisitingContext
1781    * @see UIXComponent#tearDownChildrenVisitingContext
1782    */
1783   protected final boolean invokeOnChildrenComponents(
1784     FacesContext context,
1785     String clientId,
1786     ContextCallback callback)
1787     throws FacesException
1788   {
1789     setupChildrenVisitingContext(context);
1790 
1791     boolean found = false;
1792 
1793     try
1794     {
1795       Iterator<UIComponent> children = getFacetsAndChildren();
1796 
1797       while (children.hasNext() && !found)
1798       {
1799         found = children.next().invokeOnComponent(context, clientId, callback);
1800       }
1801     }
1802     finally
1803     {
1804       tearDownChildrenVisitingContext(context);
1805     }
1806 
1807     return found;
1808   }
1809 
1810   /**
1811    * <p>
1812    * Optimized implementation of <code>invokeOnComponent</code> for NamingContainers.
1813    * If the clientId isn't within the NamingContainer, invocation of the
1814    * NamingContainer's children is skipped.
1815    * </p>
1816    * <p>Subclasses implementing NamingContainer should override
1817    * <code>invokeOnComponent</code> and delegate to this method.</p>
1818    */
1819   protected final boolean invokeOnNamingContainerComponent(
1820     FacesContext context,
1821     String clientId,
1822     ContextCallback callback)
1823     throws FacesException
1824   {
1825     assert this instanceof NamingContainer : "Only use invokeOnNamingContainerComponent on NamingContainers";
1826 
1827     boolean invokedComponent;
1828 
1829     setupVisitingContext(context);
1830 
1831     try
1832     {
1833       String thisClientId = getClientId(context);
1834 
1835       if (clientId.equals(thisClientId))
1836       {
1837         pushComponentToEL(context, null);
1838 
1839         try
1840         {
1841           // this is the component we want, so invoke the callback
1842           callback.invokeContextCallback(context, this);
1843         }
1844         finally
1845         {
1846           popComponentFromEL(context);
1847         }
1848 
1849         invokedComponent = true;
1850       }
1851       else
1852       {
1853         // if this is a NamingContainer, only traverse into it if the clientId we are looking for
1854         // is inside of it
1855         if ((!clientId.startsWith(thisClientId) ||
1856             (clientId.charAt(thisClientId.length()) != NamingContainer.SEPARATOR_CHAR)))
1857         {
1858           invokedComponent = false;
1859         }
1860         else
1861         {
1862           // iterate through children.
1863           // We inline this code instead of calling super in order
1864           // to avoid making an extra call to getClientId().
1865           invokedComponent = invokeOnChildrenComponents(context, clientId, callback);
1866         }
1867       }
1868     }
1869     finally
1870     {
1871       // teardown the context now that we have visited the children
1872       tearDownVisitingContext(context);
1873     }
1874 
1875     return invokedComponent;
1876   }
1877 
1878   /**
1879    * Override to calls the hooks for setting up and tearing down the
1880    * context before the children are visited.
1881    * @see #setupVisitingContext
1882    * @see #tearDownVisitingContext
1883    */
1884   @Override
1885   public boolean invokeOnComponent(
1886     FacesContext context,
1887     String clientId,
1888     ContextCallback callback)
1889     throws FacesException
1890   {
1891     boolean invokedComponent;
1892 
1893     // set up the context for visiting the children
1894     setupVisitingContext(context);
1895 
1896     try
1897     {
1898       String thisClientId = getClientId(context);
1899 
1900       if (clientId.equals(thisClientId))
1901       {
1902         pushComponentToEL(context, null);
1903 
1904         try
1905         {
1906           // this is the component we want, so invoke the callback
1907           callback.invokeContextCallback(context, this);
1908         }
1909         finally
1910         {
1911           popComponentFromEL(context);
1912         }
1913 
1914         // we found the component
1915         invokedComponent = true;
1916       }
1917       else
1918       {
1919         // set up the children visiting context to iterate through children. We inline this
1920         // code instead of calling super in order
1921         // to avoid making an extra call to getClientId().
1922         invokedComponent = invokeOnChildrenComponents(context, clientId, callback);
1923       }
1924     }
1925     finally
1926     {
1927       // teardown the context now that we have visited the component
1928       tearDownVisitingContext(context);
1929     }
1930 
1931     return invokedComponent;
1932   }
1933 
1934 
1935   @Override
1936   public void subscribeToEvent(Class<? extends SystemEvent> eventClass, ComponentSystemEventListener componentListener)
1937   {
1938     if (eventClass == null)
1939     {
1940         throw new NullPointerException("eventClass required");
1941     }
1942     if (componentListener == null)
1943     {
1944         throw new NullPointerException("componentListener required");
1945     }
1946 
1947     FacesBean bean = getFacesBean();
1948 
1949     AttachedObjects<Class<? extends SystemEvent>, SystemEventListener> eventStorage =
1950       (AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY);
1951 
1952     if (eventStorage == null)
1953     {
1954       eventStorage = new AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>();
1955       bean.setProperty(_SYSTEM_EVENT_LISTENERS_KEY, eventStorage);
1956     }
1957 
1958     eventStorage.addAttachedObject(eventClass, new ComponentSystemEventListenerWrapper(componentListener, this));
1959   }
1960 
1961   @Override
1962   public void unsubscribeFromEvent(Class<? extends SystemEvent> eventClass,
1963                                    ComponentSystemEventListener componentListener)
1964   {
1965     if (eventClass == null)
1966     {
1967         throw new NullPointerException("eventClass required");
1968     }
1969     if (componentListener == null)
1970     {
1971         throw new NullPointerException("componentListener required");
1972     }
1973 
1974     FacesBean bean = getFacesBean();
1975 
1976     AttachedObjects<Class<? extends SystemEvent>, SystemEventListener> eventStorage =
1977       (AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY);
1978 
1979     if (eventStorage == null)
1980     {
1981       return;
1982     }
1983 
1984     // ComponentSystemEventListenerWrapper implements equals() to compare listener and component
1985     eventStorage.removeAttachedObject(eventClass, new ComponentSystemEventListenerWrapper(componentListener, this));
1986   }
1987 
1988   @Override
1989   public List<SystemEventListener> getListenersForEventClass(Class<? extends SystemEvent> eventClass)
1990   {
1991     FacesBean bean = getFacesBean();
1992 
1993     AttachedObjects<Class<? extends SystemEvent>, SystemEventListener> eventStorage =
1994       (AttachedObjects<Class<? extends SystemEvent>, SystemEventListener>)bean.getProperty(_SYSTEM_EVENT_LISTENERS_KEY);
1995 
1996     if (eventStorage == null)
1997     {
1998       return Collections.emptyList();
1999     }
2000 
2001     return eventStorage.getAttachedObjectList(eventClass);
2002   }
2003 
2004   // ------------------------- Client behavior holder methods -------------------------
2005 
2006   /**
2007    * Utility method to assist sub-classes in the implementation of the
2008    * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface.
2009    * <p>This method must only
2010    * be called by classes that implement the interface, doing otherwise will result in an exception.
2011    * </p>
2012    * @param eventName The event name
2013    * @param behavior The behavior to add
2014    * @see javax.faces.component.behavior.ClientBehaviorHolder#addClientBehavior(String, ClientBehavior)
2015    */
2016   protected void addClientBehavior(
2017     String         eventName,
2018     ClientBehavior behavior)
2019   {
2020     // This will throw a class cast exception when illegally called by a class that does not
2021     // implement ClientBehaviorHolder
2022     Collection<String> events = ((ClientBehaviorHolder)this).getEventNames();
2023 
2024     // This will throw a null pointer exception if the component author did not correctly implement
2025     // the ClientBehaviorHolder contract which requires a non-empty collection to be returned from
2026     // getEventNames
2027     if (!events.contains(eventName))
2028     {
2029       return;
2030     }
2031 
2032     FacesBean bean = getFacesBean();
2033 
2034     AttachedObjects<String, ClientBehavior> behaviors = (
2035             AttachedObjects<String, ClientBehavior>)bean.getProperty(_CLIENT_BEHAVIORS_KEY);
2036 
2037     if (behaviors == null)
2038     {
2039       behaviors = new AttachedObjects<String, ClientBehavior>();
2040       bean.setProperty(_CLIENT_BEHAVIORS_KEY, behaviors);
2041     }
2042 
2043     behaviors.addAttachedObject(eventName, behavior);
2044   }
2045 
2046   // Note, we do not need to provide a default implementation for the event names, as client
2047   // behavior holder components must provide a non-empty list of event names. UIComponentBase
2048   // decided to return a non-valid null in their code, but that is only confusing to the user, it
2049   // is better to not implement the method and force the users to write the method upon interface
2050   // implementation.
2051   //protected Collection<String> getEventNames() {}
2052 
2053   /**
2054    * Utility method to assist sub-classes in the implementation of the
2055    * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface.
2056    * <p>This method must only
2057    * be called by classes that implement the interface, doing otherwise will result in an exception.
2058    * </p>
2059    * @see javax.faces.component.behavior.ClientBehaviorHolder#getClientBehaviors()
2060    * @return Read-only map of the client behaviors for this component
2061    */
2062   protected Map<String, List<ClientBehavior>> getClientBehaviors()
2063   {
2064     AttachedObjects<String, ClientBehavior> behaviors = (
2065             AttachedObjects<String, ClientBehavior>)getFacesBean().getProperty(_CLIENT_BEHAVIORS_KEY);
2066 
2067     if (behaviors == null)
2068     {
2069       return Collections.emptyMap();
2070     }
2071 
2072     return behaviors.getAttachedObjectMap();
2073   }
2074 
2075   /**
2076    * Utility method to assist sub-classes in the implementation of the
2077    * {@link javax.faces.component.behavior.ClientBehaviorHolder} interface.
2078    * <p>This method must only
2079    * be called by classes that implement the interface, doing otherwise will result in an exception.
2080    * </p>
2081    * @return null
2082    * @see javax.faces.component.behavior.ClientBehaviorHolder#getDefaultEventName()
2083    */
2084   protected String getDefaultEventName()
2085   {
2086     _ensureClientBehaviorHolder();
2087     return null;
2088   }
2089 
2090   private void _ensureClientBehaviorHolder()
2091   {
2092     if (!(this instanceof ClientBehaviorHolder))
2093     {
2094       throw new IllegalStateException("Component must implement ClientBehaviorHolder in order " +
2095         "to make use of this method.");
2096     }
2097   }
2098   // ------------------------- End of the client behavior holder methods -------------------------
2099 
2100   /**
2101    * <p>
2102    * This gets a single threadlocal shared stringbuilder instance, each time you call
2103    * __getSharedStringBuilder it sets the length of the stringBuilder instance to 0.
2104    * </p><p>
2105    * This allows you to use the same StringBuilder instance over and over.
2106    * You must call toString on the instance before calling __getSharedStringBuilder again.
2107    * </p>
2108    * Example that works
2109    * <pre><code>
2110    * StringBuilder sb1 = __getSharedStringBuilder();
2111    * sb1.append(a).append(b);
2112    * String c = sb1.toString();
2113    *
2114    * StringBuilder sb2 = __getSharedStringBuilder();
2115    * sb2.append(b).append(a);
2116    * String d = sb2.toString();
2117    * </code></pre>
2118    * <br><br>
2119    * Example that doesn't work, you must call toString on sb1 before
2120    * calling __getSharedStringBuilder again.
2121    * <pre><code>
2122    * StringBuilder sb1 = __getSharedStringBuilder();
2123    * StringBuilder sb2 = __getSharedStringBuilder();
2124    *
2125    * sb1.append(a).append(b);
2126    * String c = sb1.toString();
2127    *
2128    * sb2.append(b).append(a);
2129    * String d = sb2.toString();
2130    * </code></pre>
2131    *
2132    */
2133   static StringBuilder __getSharedStringBuilder()
2134   {
2135     StringBuilder sb = _STRING_BUILDER.get();
2136 
2137     if (sb == null)
2138     {
2139       sb = new StringBuilder();
2140       _STRING_BUILDER.set(sb);
2141     }
2142 
2143     // clear out the stringBuilder by setting the length to 0
2144     sb.setLength(0);
2145 
2146     return sb;
2147   }
2148 
2149   /**
2150    * render a component. this is called by renderers whose
2151    * getRendersChildren() return true.
2152    * @deprecated {@link UIComponent#encodeAll(FacesContext)} should be used instead of this method
2153    */
2154   @Deprecated
2155   void __encodeRecursive(FacesContext context, UIComponent component)
2156     throws IOException
2157   {
2158     component.encodeAll(context);
2159   }
2160 
2161   static private UIComponent _findInsideOf(
2162     UIComponent from,
2163     String id)
2164   {
2165     Iterator<UIComponent> kids = from.getFacetsAndChildren();
2166     while (kids.hasNext())
2167     {
2168       UIComponent kid = kids.next();
2169       if (id.equals(kid.getId()))
2170         return kid;
2171 
2172       if (!(kid instanceof NamingContainer))
2173       {
2174         UIComponent returned = _findInsideOf(kid, id);
2175         if (returned != null)
2176           return returned;
2177       }
2178     }
2179 
2180     return null;
2181   }
2182 
2183   /**
2184    * <p>Verify that the specified component id is safe to add to the tree.
2185    * </p>
2186    *
2187    * @param id The proposed component id to check for validity
2188    *
2189    * @exception IllegalArgumentException if <code>id</code>
2190    *  is <code>null</code> or contains invalid characters
2191    */
2192   private void _validateId(String id)
2193   {
2194     if (id == null)
2195       return;
2196 
2197 
2198     int n = id.length();
2199     if (0 == n ||
2200         NamingContainer.SEPARATOR_CHAR == id.charAt(0))
2201       _throwBadId(id);
2202 
2203     for (int i = 0; i < n; i++)
2204     {
2205       char c = id.charAt(i);
2206       if (i == 0)
2207       {
2208         if (!Character.isLetter(c) && (c != '_'))
2209           _throwBadId(id);
2210       }
2211       else
2212       {
2213         if (!(Character.isLetter(c) ||
2214               Character.isDigit(c) ||
2215               (c == '-') || (c == '_')))
2216         {
2217           _throwBadId(id);
2218         }
2219       }
2220     }
2221   }
2222 
2223   private void _throwBadId(String id)
2224   {
2225     throw new IllegalArgumentException(_LOG.getMessage(
2226       "ILLEGAL_ID", id));
2227   }
2228 
2229   private void _init(
2230     String rendererType)
2231   {
2232     FacesBean oldBean = _facesBean;
2233     FacesBean newBean = createFacesBean(rendererType);;
2234 
2235     if (oldBean != null)
2236       newBean.addAll(oldBean);
2237 
2238     _attributes = new ValueMap(newBean);
2239 
2240     _facesBean = newBean;
2241 
2242     // determine whether it is ok to store the attributes locally.  We cache the result since
2243     // this can be a little involved
2244     boolean usesFacesBeanImpl = false;
2245 
2246     if (newBean instanceof UIXFacesBeanImpl)
2247       usesFacesBeanImpl = true;
2248     else
2249     {
2250       // handle the wrapped case
2251       FacesBean currImpl = newBean;
2252 
2253       while (currImpl instanceof FacesBeanWrapper)
2254       {
2255         currImpl = ((FacesBeanWrapper)currImpl).getWrappedBean();
2256 
2257         if (currImpl instanceof UIXFacesBeanImpl)
2258         {
2259           usesFacesBeanImpl = true;
2260           break;
2261         }
2262       }
2263     }
2264 
2265     _usesFacesBeanImpl = usesFacesBeanImpl;
2266   }
2267 
2268   private FacesBean                _facesBean;
2269   private List<UIComponent>        _children;
2270   private Map<String, Object>      _attributes;
2271   private Map<String, UIComponent> _facets;
2272   private UIComponent              _parent;
2273   private String                   _id;
2274   private String                   _clientId;
2275   private boolean                  _usesFacesBeanImpl;
2276 
2277   // Cached instance of the Renderer for this component.
2278   // The instance will be re-retrieved in encodeBegin()
2279   private transient Renderer _cachedRenderer = _UNDEFINED_RENDERER;
2280   private transient LifecycleRenderer _cachedLifecycleRenderer =
2281                                                 _UNDEFINED_LIFECYCLE_RENDERER;
2282 
2283   // -= Simon Lessard =-
2284   // FIXME: _initialStateMarked is never read
2285   //        So commented out, is that ok? If so, this attribute should be deleted
2286   //private transient boolean _initialStateMarked;
2287 
2288   private static final Iterator<String> _EMPTY_STRING_ITERATOR = CollectionUtils.emptyIterator();
2289   private static final Iterator<UIComponent> _EMPTY_UICOMPONENT_ITERATOR =
2290     CollectionUtils.emptyIterator();
2291 
2292   static private final ThreadLocal<StringBuilder> _STRING_BUILDER =
2293                                                           ThreadLocalUtils.newRequestThreadLocal();
2294 
2295   static private FacesBean.Type _createType()
2296   {
2297     try
2298     {
2299       ClassLoader cl = _getClassLoader();
2300       URL url = cl.getResource("META-INF/faces-bean-type.properties");
2301       if (url != null)
2302       {
2303         Properties properties = new Properties();
2304         InputStream is = url.openStream();
2305         try
2306         {
2307           properties.load(is);
2308           String className = (String)
2309             properties.get(UIXComponentBase.class.getName());
2310           return (FacesBean.Type) cl.loadClass(className).newInstance();
2311         }
2312         finally
2313         {
2314           is.close();
2315         }
2316       }
2317     }
2318     catch (Exception e)
2319     {
2320       _LOG.severe("CANNOT_LOAD_TYPE_PROPERTIES", e);
2321     }
2322 
2323     // For testing purposes, return a valid Type
2324     return new FacesBean.Type();
2325   }
2326 
2327   static private ClassLoader _getClassLoader()
2328   {
2329     ClassLoader loader = Thread.currentThread().getContextClassLoader();
2330     if (loader == null)
2331       loader = FacesBeanFactory.class.getClassLoader();
2332     return loader;
2333   }
2334 
2335   static private class RendererImpl extends Renderer
2336   {
2337   }
2338 
2339   static private class ExtendedRendererImpl extends ExtendedRenderer
2340   {
2341   }
2342 
2343   private static boolean _isClientIdCachingEnabled()
2344   {
2345     return (_CLIENT_ID_CACHING != ClientIdCaching.OFF);
2346   }
2347 
2348   private static boolean _isClientIdDebuggingEnabled()
2349   {
2350     return (_CLIENT_ID_CACHING == ClientIdCaching.DEBUG);
2351   }
2352 
2353   // Warn if caching is disabled + production environment since this is
2354   // undesirable from a performance perspective.
2355   private static void _warnClientIdCachingConfig(FacesContext context)
2356   {
2357     if (_CLIENT_ID_CACHING != ClientIdCaching.ON &&
2358          context.isProjectStage(ProjectStage.Production) &&
2359          !_warnedClientIdCachingConfig(context))
2360     {
2361       _LOG.warning(
2362         "The org.apache.myfaces.trinidad.CLIENT_ID_CACHING system property is set to: " +
2363          _CLIENT_ID_CACHING +
2364         ".  For best performance, client id caching should be ON in production environments.");
2365 
2366       _clientIdCachingConfigWarned(context);
2367     }
2368   }
2369 
2370   // Tests whether we have already warned about the caching config.
2371   // We only want to warn once per run.
2372   private static boolean _warnedClientIdCachingConfig(FacesContext context)
2373   {
2374     ExternalContext external = context.getExternalContext();
2375     Map<String, Object> appMap = external.getApplicationMap();
2376 
2377     return Boolean.TRUE.equals(appMap.get(_WARNED_CLIENT_ID_CACHING_KEY));
2378   }
2379 
2380   // Marks the fact that we have now warned about the caching config.
2381   private static void _clientIdCachingConfigWarned(FacesContext context)
2382   {
2383     ExternalContext external = context.getExternalContext();
2384     Map<String, Object> appMap = external.getApplicationMap();
2385 
2386     appMap.put(_WARNED_CLIENT_ID_CACHING_KEY, Boolean.TRUE);
2387   }
2388 
2389   // Utility for deriving initial CLIENT_ID_CACHING value.
2390   private static ClientIdCaching _initClientIdCaching()
2391   {
2392     String cachingProperty = _getClientIdCachingSystemProperty();
2393     ClientIdCaching caching = _toClientIdCachingEnum(cachingProperty);
2394 
2395     _LOG.config("Client id caching configuration: " + caching);
2396 
2397     return caching;
2398   }
2399 
2400   private static String _getClientIdCachingSystemProperty()
2401   {
2402     try
2403     {
2404       return System.getProperty(_SYSTEM_PROP_CLIENT_ID_CACHING);
2405     }
2406     catch (Throwable t)
2407     {
2408       _LOG.warning(t);
2409     }
2410 
2411     return null;
2412   }
2413 
2414   private static ClientIdCaching _toClientIdCachingEnum(String cachingProperty)
2415   {
2416     try
2417     {
2418       return Enum.valueOf(ClientIdCaching.class,
2419                            (cachingProperty == null) ?
2420                              "ON" :
2421                              cachingProperty.toUpperCase());
2422     }
2423     catch (IllegalArgumentException e)
2424     {
2425       _LOG.warning("Invalid value specified for " +
2426                    _SYSTEM_PROP_CLIENT_ID_CACHING +
2427                    " system property: " +
2428                    cachingProperty +
2429                    ". Valid values are: on | off | debug.");
2430 
2431     }
2432 
2433     return ClientIdCaching.ON;
2434   }
2435 
2436   // Little enum for tracking the client id caching behavior.
2437   private enum ClientIdCaching
2438   {
2439     ON,
2440     OFF,
2441     DEBUG
2442   }
2443 
2444 
2445   private static class ComponentSystemEventListenerWrapper implements SystemEventListener, StateHolder
2446   {
2447     ComponentSystemEventListenerWrapper(ComponentSystemEventListener listener, UIComponent component)
2448     {
2449       _delegate = listener;
2450       _componentClass = component.getClass();
2451     }
2452 
2453     // Default constructor for state restoration
2454     public ComponentSystemEventListenerWrapper()
2455     {
2456     }
2457 
2458     @Override
2459     public boolean equals(Object o)
2460     {
2461       if (o == this)
2462       {
2463         return true;
2464       }
2465       else if (o instanceof ComponentSystemEventListenerWrapper)
2466       {
2467         ComponentSystemEventListenerWrapper other = (ComponentSystemEventListenerWrapper) o;
2468         return _componentClass.equals(other._componentClass) && _delegate.equals(other._delegate);
2469       }
2470 
2471       return false;
2472     }
2473 
2474     @Override
2475     public int hashCode()
2476     {
2477       return _componentClass.hashCode() + _delegate.hashCode();
2478     }
2479 
2480     // SystemEventListener implementation
2481 
2482     @Override
2483     public void processEvent(SystemEvent event) throws AbortProcessingException
2484     {
2485       assert (event instanceof ComponentSystemEvent);
2486       _delegate.processEvent((ComponentSystemEvent)event);
2487     }
2488 
2489     @Override
2490     public boolean isListenerForSource(Object source)
2491     {
2492       if (_delegate instanceof SystemEventListener)
2493       {
2494         return ((SystemEventListener)_delegate).isListenerForSource(source);
2495       }
2496 
2497       // From the spec: and its implementation of SystemEventListener.isListenerForSource(java.lang.Object) must return true
2498       // if the instance class of this UIComponent is assignable from the argument to isListenerForSource.
2499       return _componentClass.isAssignableFrom(source.getClass());
2500     }
2501 
2502     // StateHolder Implementation
2503 
2504     @Override
2505     public Object saveState(FacesContext context)
2506     {
2507       if (_delegate instanceof UIComponent)
2508       {
2509         return null;
2510       }
2511 
2512       Object[] state = new Object[2];
2513       state[0] = StateUtils.saveStateHolder(context, _delegate);
2514       state[1] = _componentClass;
2515 
2516       return state;
2517     }
2518 
2519     @Override
2520     public void restoreState(FacesContext context, Object state)
2521     {
2522       if (state == null)
2523       {
2524         return;
2525       }
2526 
2527       Object[] stateArr = (Object[]) state;
2528       Object saved = stateArr[0];
2529 
2530       _delegate = (ComponentSystemEventListener) ((saved == null) ? UIComponent .getCurrentComponent(context)
2531                                                 : StateUtils.restoreStateHolder(context, saved));
2532       _componentClass = (Class<?>)stateArr[1];
2533     }
2534 
2535     @Override
2536     public boolean isTransient()
2537     {
2538       if (_delegate instanceof StateHolder)
2539       {
2540           return ((StateHolder)_delegate).isTransient();
2541       }
2542       return false;
2543     }
2544 
2545     @Override
2546     public void setTransient(boolean isTransient)
2547     {
2548       if (_delegate instanceof StateHolder)
2549       {
2550         ((StateHolder)_delegate).setTransient(isTransient);
2551       }
2552     }
2553 
2554 
2555     private ComponentSystemEventListener _delegate;
2556     private Class<?> _componentClass;
2557   }
2558 
2559   private static final String _NO_SUBTREE_CHANGE_ATTR = "_trinNoSubtreeChange";
2560   
2561   private static final ClientIdCaching _CLIENT_ID_CACHING = _initClientIdCaching();
2562 
2563   // System property controlling whether client ID caching is enabled
2564   private static final String _SYSTEM_PROP_CLIENT_ID_CACHING =
2565     "org.apache.myfaces.trinidad.CLIENT_ID_CACHING";
2566 
2567   // Application map key indicating that we've already warned once
2568   // that the client id caching configuration is not optimized for
2569   // production mode.
2570   private static final String _WARNED_CLIENT_ID_CACHING_KEY =
2571     "org.apache.myfaces.trinidad.WARNED_CLIENT_ID_CACHING";
2572 
2573   static private final LifecycleRenderer _UNDEFINED_LIFECYCLE_RENDERER =
2574                                                 new ExtendedRendererImpl();
2575   static private final Renderer _UNDEFINED_RENDERER = new RendererImpl();
2576 }