View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package javax.faces.component;
20  
21  import java.io.IOException;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collection;
25  import java.util.Collections;
26  import java.util.HashMap;
27  import java.util.Iterator;
28  import java.util.LinkedList;
29  import java.util.List;
30  import java.util.Locale;
31  import java.util.Map;
32  import java.util.logging.Level;
33  import java.util.logging.Logger;
34  
35  import javax.el.MethodExpression;
36  import javax.el.ValueExpression;
37  import javax.faces.FactoryFinder;
38  import javax.faces.application.ProjectStage;
39  import javax.faces.context.FacesContext;
40  import javax.faces.context.PartialViewContext;
41  import javax.faces.event.AbortProcessingException;
42  import javax.faces.event.ExceptionQueuedEvent;
43  import javax.faces.event.ExceptionQueuedEventContext;
44  import javax.faces.event.FacesEvent;
45  import javax.faces.event.PhaseEvent;
46  import javax.faces.event.PhaseId;
47  import javax.faces.event.PhaseListener;
48  import javax.faces.event.PostConstructViewMapEvent;
49  import javax.faces.event.PreDestroyViewMapEvent;
50  import javax.faces.event.SystemEvent;
51  import javax.faces.event.SystemEventListener;
52  import javax.faces.lifecycle.Lifecycle;
53  import javax.faces.lifecycle.LifecycleFactory;
54  import javax.faces.view.ViewDeclarationLanguage;
55  import javax.faces.view.ViewMetadata;
56  import javax.faces.webapp.FacesServlet;
57  
58  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
59  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFJspProperty;
60  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
61  
62  /**
63   * Creates a JSF View, which is a container that holds all of the components that are part of the view.
64   * <p>
65   * Unless otherwise specified, all attributes accept static values or EL expressions.
66   * </p>
67   * <p>
68   * See the javadoc for this class in the <a href="http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html">JSF
69   * Specification</a> for further details.
70   * </p>
71   */
72  @JSFComponent(name = "f:view", bodyContent = "JSP", tagClass = "org.apache.myfaces.taglib.core.ViewTag")
73  @JSFJspProperty(name = "binding", returnType = "java.lang.String", tagExcluded = true)
74  public class UIViewRoot extends UIComponentBase implements UniqueIdVendor
75  {
76      public static final String COMPONENT_FAMILY = "javax.faces.ViewRoot";
77      public static final String COMPONENT_TYPE = "javax.faces.ViewRoot";
78      public static final String METADATA_FACET_NAME = "javax_faces_metadata";
79      public static final String UNIQUE_ID_PREFIX = "j_id";
80      public static final String VIEW_PARAMETERS_KEY = "javax.faces.component.VIEW_PARAMETERS_KEY";
81  
82      private transient Logger logger = null;
83  
84      private static final PhaseProcessor APPLY_REQUEST_VALUES_PROCESSOR = new ApplyRequestValuesPhaseProcessor();
85      private static final PhaseProcessor PROCESS_VALIDATORS_PROCESSOR = new ProcessValidatorPhaseProcessor();
86      private static final PhaseProcessor UPDATE_MODEL_PROCESSOR = new UpdateModelPhaseProcessor();
87  
88      /**
89       * The counter which will ensure a unique component id for every component instance in the tree that doesn't have an
90       * id attribute set.
91       */
92      //private long _uniqueIdCounter = 0;
93  
94      // todo: is it right to save the state of _events and _phaseListeners?
95      private List<FacesEvent> _events;
96  
97      /**
98       * Map containing view scope objects. 
99       * 
100      * It is not expected this map hold PartialStateHolder instances,
101      * so we can use saveAttachedState and restoreAttachedState methods.
102      */
103     private Map<String, Object> _viewScope;
104 
105     private transient Lifecycle _lifecycle = null;
106     
107     private HashMap<Class<? extends SystemEvent>, List<SystemEventListener>> _systemEventListeners;
108     
109     // Tracks success in the beforePhase. Listeners that threw an exception
110     // in beforePhase or were never called, because a previous listener threw
111     // an exception, should not have their afterPhase method called
112     private transient Map<PhaseId, boolean[]> listenerSuccessMap;
113     
114     private static final String JAVAX_FACES_LOCATION_PREFIX = "javax_faces_location_";
115     private static final String JAVAX_FACES_LOCATION_HEAD = "javax_faces_location_head";
116     private static final String JAVAX_FACES_LOCATION_BODY = "javax_faces_location_body";
117     private static final String JAVAX_FACES_LOCATION_FORM = "javax_faces_location_form";
118     
119     private transient boolean _resourceDependencyUniqueId;
120     private transient Map<String,Object> _attributesMap;
121     
122     /**
123      * Construct an instance of the UIViewRoot.
124      */
125     public UIViewRoot()
126     {
127         setRendererType(null);
128     }
129 
130     /**
131      * @since 2.0
132      */
133     public void addComponentResource(FacesContext context, UIComponent componentResource)
134     {
135         addComponentResource(context, componentResource, null);
136     }
137 
138     /**
139      * @since 2.0
140      */
141     public void addComponentResource(FacesContext context, UIComponent componentResource, String target)
142     {
143         // If the target argument is null
144         if (target == null)
145         {
146             // Look for a target attribute on the component
147             target = (String)componentResource.getAttributes().get("target");
148 
149             // If there is no target attribute, set target to be the default value head
150             if (target == null)
151             {
152                 target = "head";
153             }
154         }
155 
156         // Call getComponentResources to obtain the child list for the given target
157         List<UIComponent> componentResources = _getComponentResources(context, target);
158 
159         // If the component ID of componentResource matches the ID of a resource
160         // that has already been added, remove the old resource.
161         String componentId = componentResource.getId();
162         
163         if (componentId == null)
164         {
165             // componentResource can have no id - calling createUniqueId makes us sure that component will have one
166             // https://issues.apache.org/jira/browse/MYFACES-2775
167             componentId = createUniqueId(context, null);
168             componentResource.setId(componentId);
169         }
170         
171         // This var helps to handle the case when we try to add a component that already is
172         // on the resource list, because PostAddToViewEvent also is sent to components 
173         // backing resources. The problem start when a component is already inside
174         // componentResources list and we try to relocate it again. This leads to a StackOverflowException
175         // so we need to check if a component is and prevent remove and add it again. Note
176         // that remove and then add a component trigger another PostAddToViewEvent. The right
177         // point to prevent this StackOverflowException is here, because this method is 
178         // responsible to traverse the componentResources list and add when necessary.
179         boolean alreadyAdded = false;
180 
181         //The check is only necessary if the component resource is part of the tree.
182         if (componentResource.isInView())
183         {
184             if (componentResource.getParent() != null &&
185                 componentResource.getParent().getId() != null &&
186                 componentResource.getParent().getId().equals(JAVAX_FACES_LOCATION_PREFIX + target))
187             {
188                 // We can assume safely that the component is in place, because there is no way to 
189                 // put a component resource on a component resource container without call addComponentResource
190                 // so relocation here will not happen.
191                 alreadyAdded = true;
192             }
193             else if (componentId != null)
194             {
195                 for(Iterator<UIComponent> it = componentResources.iterator(); it.hasNext();)
196                 {
197                     UIComponent component = it.next();
198                     if(componentId.equals(component.getId()) && componentResource != component)
199                     {
200                         it.remove();
201                     }
202                     else if (componentResource == component)
203                     {
204                         alreadyAdded = true;
205                     }
206                 }
207             }
208         }
209         else if (componentId != null)
210         {
211             for(Iterator<UIComponent> it = componentResources.iterator(); it.hasNext();)
212             {
213                 UIComponent component = it.next();
214                 if(componentId.equals(component.getId()) && componentResource != component)
215                 {
216                     it.remove();
217                 }
218                 else if (componentResource == component)
219                 {
220                     alreadyAdded = true;
221                 }
222             }
223         }
224         
225         // Add the component resource to the list
226         if (!alreadyAdded)
227         {
228             componentResources.add(componentResource);
229         }
230     }
231 
232     /**
233      * Adds a The phaseListeners attached to ViewRoot.
234      */
235     public void addPhaseListener(PhaseListener phaseListener)
236     {
237         if (phaseListener == null)
238         {
239             throw new NullPointerException("phaseListener");
240         }
241         
242         getStateHelper().add(PropertyKeys.phaseListeners, phaseListener);
243     }
244 
245     /**
246      * @since 2.0
247      */
248     public void broadcastEvents(FacesContext context, PhaseId phaseId)
249     {
250         if (_events == null)
251         {
252             return;
253         }
254         
255         Events events = _getEvents(phaseId);
256         
257         // Spec. 3.4.2.6 Event Broadcasting:
258         // Queue one or more additional events, from the same source
259         // component or a different one, for processing during the
260         // current lifecycle phase.
261         
262         // Unfortunately with that requirement it is easy to create infinite loop in processing. One example can be:
263         //
264         // public processAction(ActionEvent actionEvent)
265         // {
266         // actionEvent  = new ActionEvent(actionEvent.getComponent());
267         // actionEvent.queue();
268         // }
269         // 
270         // Thus we iterate here only 15x. If iteration overreachs 15 we output a warning  
271         
272         int loops = 0;
273         int maxLoops = 15;
274         Collection<FacesEvent> eventsAborted = new LinkedList<FacesEvent>(); 
275         do
276         {
277             // First broadcast events that have been queued for PhaseId.ANY_PHASE.
278             boolean noUnexpectedException = _broadcastAll(context, events.getAnyPhase(), eventsAborted);
279             if (!noUnexpectedException)
280             {
281                 return;
282             }
283             List<FacesEvent> eventsOnPhase = events.getOnPhase();
284             if (!eventsAborted.isEmpty())
285             {
286                 eventsOnPhase.removeAll(eventsAborted);
287                 eventsAborted.clear();
288             }
289             noUnexpectedException = _broadcastAll(context, eventsOnPhase, eventsAborted);
290             if (!noUnexpectedException)
291             {
292                 return;
293             }
294 
295             events = _getEvents(phaseId);
296             loops++;
297             
298         } while (events.hasMoreEvents() && loops < maxLoops);
299         
300         if (loops == maxLoops && events.hasMoreEvents())
301         {
302             // broadcast reach maxLoops - probably a infinitive recursion:
303             boolean production = getFacesContext().isProjectStage(ProjectStage.Production);
304             Level level = production ? Level.FINE : Level.WARNING;
305             if (_getLogger().isLoggable(level))
306             {
307                 List<String> name = new ArrayList<String>(events.getAnyPhase().size() + events.getOnPhase().size());
308                 for (FacesEvent facesEvent : events.getAnyPhase())
309                 {
310                     String clientId = facesEvent.getComponent().getClientId(getFacesContext());
311                     name.add(clientId);
312                 }
313                 for (FacesEvent facesEvent : events.getOnPhase())
314                 {
315                     String clientId = facesEvent.getComponent().getClientId(getFacesContext());
316                     name.add(clientId);
317                 }
318                 _getLogger().log(level,
319                         "Event broadcating for PhaseId {0} at UIViewRoot {1} reaches maximal limit, please check " +
320                         "listeners for infinite recursion. Component id: {2}",
321                         new Object [] {phaseId, getViewId(), name});
322             }
323         }
324     }
325 
326 
327     /**
328      * Provides a unique id for this component instance.
329      */
330     public String createUniqueId()
331     {
332         return createUniqueId(getFacesContext(), null);
333     }
334 
335     /**
336      * 
337      * {@inheritDoc}
338      * 
339      * @since 2.0
340      */
341     public String createUniqueId(FacesContext context, String seed)
342     {
343         StringBuilder bld = __getSharedStringBuilder(context);
344 
345         // Generate an identifier for a component. The identifier will be prefixed with
346         // UNIQUE_ID_PREFIX, and will be unique within this UIViewRoot.
347         if(seed==null)
348         {
349             if (isResourceDependencyUniqueId())
350             {
351                 Long uniqueIdCounter = (Long) getStateHelper().get(PropertyKeys.resourceDependencyUniqueIdCounter);
352                 uniqueIdCounter = (uniqueIdCounter == null) ? 0 : uniqueIdCounter;
353                 getStateHelper().put(PropertyKeys.resourceDependencyUniqueIdCounter, (uniqueIdCounter+1L));
354                 return bld.append(UNIQUE_ID_PREFIX).append("__rd_").append(uniqueIdCounter).toString();
355             }
356             else
357             {
358                 Long uniqueIdCounter = (Long) getStateHelper().get(PropertyKeys.uniqueIdCounter);
359                 uniqueIdCounter = (uniqueIdCounter == null) ? 0 : uniqueIdCounter;
360                 getStateHelper().put(PropertyKeys.uniqueIdCounter, (uniqueIdCounter+1L));
361                 return bld.append(UNIQUE_ID_PREFIX).append("__v_").append(uniqueIdCounter).toString();
362             }
363         }
364         // Optionally, a unique seed value can be supplied by component creators which
365         // should be included in the generated unique id.
366         else
367         {
368             return bld.append(UNIQUE_ID_PREFIX).append(seed).toString();
369         }
370     }
371 
372     @Override
373     public void encodeBegin(FacesContext context) throws IOException
374     {
375         checkNull(context, "context");
376 
377         boolean skipPhase = false;
378 
379         try
380         {
381             skipPhase = notifyListeners(context, PhaseId.RENDER_RESPONSE, getBeforePhaseListener(), true);
382         }
383         catch (Exception e)
384         {
385             // following the spec we have to swallow the exception
386             _getLogger().log(Level.SEVERE, "Exception while processing phase listener: " + e.getMessage(), e);
387         }
388 
389         if (!skipPhase)
390         {
391             //prerendering happens, we now publish the prerender view event
392             //the specs states that the viewroot as source is about to be rendered
393             //hence we issue the event immediately before publish, if the phase is not skipped
394             //context.getApplication().publishEvent(context, PreRenderViewEvent.class, this);
395             //then the view rendering is about to begin
396             super.encodeBegin(context);
397         }
398         else
399         {
400             pushComponentToEL(context, this);
401         }
402     }
403 
404     /**
405      * @since 2.0
406      */
407     @Override
408     public void encodeChildren(FacesContext context) throws IOException
409     {
410         if (context.getResponseComplete())
411         {
412             return;
413         }
414         PartialViewContext pContext = context.getPartialViewContext();
415         
416         // If PartialViewContext.isAjaxRequest() returns true
417         if (pContext.isAjaxRequest())
418         {
419             // Perform partial rendering by calling PartialViewContext.processPartial() with PhaseId.RENDER_RESPONSE.
420             //sectin 13.4.3 of the jsf2 specification
421             pContext.processPartial(PhaseId.RENDER_RESPONSE);
422         }
423         else
424         {
425             // If PartialViewContext.isAjaxRequest() returns false
426             // delegate to super.encodeChildren(javax.faces.context.FacesContext) method.
427             super.encodeChildren(context);
428         }
429     }
430 
431     @Override
432     public void encodeEnd(FacesContext context) throws IOException
433     {
434         checkNull(context, "context");
435 
436         if (!context.getResponseComplete())
437         {
438             super.encodeEnd(context);
439             
440             // the call to encodeAll() on every UIViewParameter here is only necessary
441             // if the current request is _not_ an AJAX request, because if it was an
442             // AJAX request, the call would already have happened in PartialViewContextImpl and
443             // would anyway be too late here, because the state would already have been generated
444             PartialViewContext partialContext = context.getPartialViewContext();
445             if (!partialContext.isAjaxRequest())
446             {
447                 ViewDeclarationLanguage vdl
448                         = context.getApplication().getViewHandler().getViewDeclarationLanguage(context, getViewId());
449                 if (vdl != null)
450                 {
451                     // If the current view has view parameters, as indicated by a non-empty
452                     // and non-UnsupportedOperationException throwing
453                     // return from ViewDeclarationLanguage.getViewMetadata(javax.faces.context.FacesContext, String)
454                     ViewMetadata metadata = null;
455                     try
456                     {
457                         metadata = vdl.getViewMetadata(context, getViewId());    
458                     }
459                     catch(UnsupportedOperationException e)
460                     {
461                         _getLogger().log(Level.SEVERE, "Exception while obtaining the view metadata: " +
462                                 e.getMessage(), e);
463                     }
464                     
465                     if (metadata != null)
466                     {
467                         try
468                         {
469                             Collection<UIViewParameter> viewParams = ViewMetadata.getViewParameters(this);    
470                             if(!viewParams.isEmpty())
471                             {
472                                 // call UIViewParameter.encodeAll(javax.faces.context.FacesContext) on each parameter.
473                                 for(UIViewParameter param : viewParams)
474                                 {
475                                     param.encodeAll(context);
476                                 }
477                             }
478                         }
479                         catch(UnsupportedOperationException e)
480                         {
481                             // If calling getViewParameters() causes UnsupportedOperationException
482                             // to be thrown, the exception must be silently swallowed.
483                         }
484                     }
485                 }
486             }
487         }
488         
489         try
490         {
491             notifyListeners(context, PhaseId.RENDER_RESPONSE, getAfterPhaseListener(), false);
492         }
493         catch (Exception e)
494         {
495             // following the spec we have to swallow the exception
496             _getLogger().log(Level.SEVERE, "Exception while processing phase listener: " + e.getMessage(), e);
497         }
498     }
499 
500     /**
501      * MethodBinding pointing to a method that takes a javax.faces.event.PhaseEvent and returns void, called after every
502      * phase except for restore view.
503      *
504      * @return the new afterPhaseListener value
505      */
506     @JSFProperty(returnSignature = "void", methodSignature = "javax.faces.event.PhaseEvent",
507                  jspName = "afterPhase", stateHolder=true)
508     public MethodExpression getAfterPhaseListener()
509     {
510         return (MethodExpression) getStateHelper().eval(PropertyKeys.afterPhaseListener);
511     }
512 
513     /**
514      * MethodBinding pointing to a method that takes a javax.faces.event.PhaseEvent and returns void, called before
515      * every phase except for restore view.
516      *
517      * @return the new beforePhaseListener value
518      */
519     @JSFProperty(returnSignature = "void", methodSignature = "javax.faces.event.PhaseEvent",
520                  jspName = "beforePhase", stateHolder=true)
521     public MethodExpression getBeforePhaseListener()
522     {
523         return (MethodExpression) getStateHelper().eval(PropertyKeys.beforePhaseListener);
524     }
525 
526     /**
527      * DO NOT USE.
528      * <p>
529      * As this component has no "id" property, it has no clientId property either.
530      */
531     @Override
532     public String getClientId(FacesContext context)
533     {
534         return super.getClientId(context);
535         // Call parent method due to TCK problems
536         // return null;
537     }
538 
539     /**
540      * @since 2.0
541      */
542     public List<UIComponent> getComponentResources(FacesContext context, String target)
543     {
544         // Locate the facet for the component by calling getFacet() using target as the argument
545         UIComponent facet = getFacet(target);
546 
547         /*
548         // If the facet is not found,
549         if (facet == null)
550         {
551             // create the facet by calling context.getApplication().createComponent()
552             // using javax.faces.Panel as the argument
553             facet = context.getApplication().createComponent("javax.faces.Panel");
554 
555             // Set the id of the facet to be target
556             facet.setId(target);
557 
558             // Add the facet to the facets Map using target as the key
559             getFacets().put(target, facet);
560         }
561 
562         // Return the children of the facet
563         // The API doc indicates that this method should "Return an unmodifiable
564         // List of UIComponents for the provided target argument."
565         // and also that "If no children are found for the facet, return Collections.emptyList()."
566         List<UIComponent> children = facet.getChildren();
567         return ( children == null ? Collections.<UIComponent>emptyList() : Collections.unmodifiableList(children) );
568         */
569         if (facet != null)
570         {
571             if (facet.getChildCount() > 0)
572             {
573                 return Collections.unmodifiableList(facet.getChildren());
574             }
575             else
576             {
577                 return Collections.<UIComponent>emptyList();
578             }
579         }
580         return Collections.<UIComponent>emptyList();
581     }
582     
583     private List<UIComponent> _getComponentResources(FacesContext context, String target)
584     {
585         // Locate the facet for the component by calling getFacet() using target as the argument
586         UIComponent facet = getFacet(target);
587 
588         // If the facet is not found,
589         if (facet == null)
590         {
591             // create the facet by calling context.getApplication().createComponent()
592             // using javax.faces.Panel as the argument
593             facet = context.getApplication().createComponent("javax.faces.ComponentResourceContainer");
594 
595             // Set the id of the facet to be target
596             if (target.equals("head"))
597             {
598                 facet.setId(JAVAX_FACES_LOCATION_HEAD);
599             }
600             else if (target.equals("body"))
601             {
602                 facet.setId(JAVAX_FACES_LOCATION_BODY);
603             }
604             else if (target.equals("form"))
605             {
606                 facet.setId(JAVAX_FACES_LOCATION_FORM);
607             }
608             else
609             {
610                 facet.setId(JAVAX_FACES_LOCATION_PREFIX + target);
611             }
612             
613             // From jsr-314-open list it was made clear this facet is transient,
614             // because all component resources does not change its inner state between
615             // requests
616             //
617             // MYFACES-3047 It was found that resources added using ResourceDependency annotation
618             // requires to be saved and restored, so it is not possible to mark this facets
619             // as transient. The previous statement is true only for PSS.
620             //facet.setTransient(true);
621 
622             // Add the facet to the facets Map using target as the key
623             getFacets().put(target, facet);
624         }
625         return facet.getChildren();
626     }
627 
628     @Override
629     public String getFamily()
630     {
631         return COMPONENT_FAMILY;
632     }
633 
634     /**
635      * The locale for this view.
636      * <p>
637      * Defaults to the default locale specified in the faces configuration file.
638      * </p>
639      */
640     @JSFProperty
641     public Locale getLocale()
642     {
643         Object locale = getStateHelper().get(PropertyKeys.locale);
644         if (locale != null)
645         {
646             return (Locale)locale;
647         }
648         ValueExpression expression = getValueExpression(PropertyKeys.locale.toString());
649         if (expression != null)
650         {
651             Object veLocale = expression.getValue(getFacesContext().getELContext());
652             if (veLocale instanceof Locale)
653             {
654                 return (Locale) veLocale;
655             }
656             else
657             {
658                 return (Locale) _LocaleUtils.toLocale(veLocale.toString());
659             }
660         }
661         else
662         {
663             locale = getFacesContext().getApplication().getViewHandler().calculateLocale(getFacesContext());
664 
665             if (locale instanceof Locale)
666             {
667                 return (Locale)locale;
668             }
669             else if (locale instanceof String)
670             {
671                 return _LocaleUtils.toLocale((String)locale);
672             }
673         }
674 
675         return getFacesContext().getApplication().getViewHandler().calculateLocale(getFacesContext());
676     }
677 
678     /**
679      * @since 2.0
680      */
681     public List<PhaseListener> getPhaseListeners()
682     {
683         List<PhaseListener> listeners = (List<PhaseListener>) getStateHelper().get(PropertyKeys.phaseListeners);
684         if (listeners == null)
685         {
686             listeners = Collections.emptyList();
687         }
688         else
689         {
690             listeners = Collections.unmodifiableList(listeners);
691         }
692 
693         return listeners;
694     }
695 
696     /**
697      * Defines what renderkit should be used to render this view.
698      */
699     @JSFProperty
700     public String getRenderKitId()
701     {
702         return (String) getStateHelper().eval(PropertyKeys.renderKitId);
703     }
704 
705     /**
706      * @since 2.0
707      */
708     @Override
709     public boolean getRendersChildren()
710     {
711         // Call UIComponentBase.getRendersChildren() 
712         // If PartialViewContext.isAjaxRequest()  returns true this method must return true.
713         PartialViewContext context = getFacesContext().getPartialViewContext();
714 
715         return (context.isAjaxRequest()) ? true : super.getRendersChildren();
716     }
717 
718     /**
719      * A unique identifier for the "template" from which this view was generated.
720      * <p>
721      * Typically this is the filesystem path to the template file, but the exact details are the responsibility of the
722      * current ViewHandler implementation.
723      */
724     @JSFProperty(tagExcluded = true)
725     public String getViewId()
726     {
727         return (String) getStateHelper().get(PropertyKeys.viewId);
728     }
729 
730     /**
731      * @since 2.0
732      */
733     public Map<String, Object> getViewMap()
734     {
735         return this.getViewMap(true);
736     }
737 
738     /**
739      * @since 2.0
740      */
741     public Map<String, Object> getViewMap(boolean create)
742     {
743         if (_viewScope == null && create)
744         {
745             _viewScope = new ViewScope();
746             FacesContext facesContext = getFacesContext();
747             facesContext.getApplication().publishEvent(facesContext, PostConstructViewMapEvent.class, this);
748         }
749 
750         return _viewScope;
751     }
752     
753     /**
754      * {@inheritDoc}
755      */
756     @Override
757     public boolean isInView()
758     {
759         return true;
760     }
761 
762     public void processApplication(final FacesContext context)
763     {
764         checkNull(context, "context");
765         _process(context, PhaseId.INVOKE_APPLICATION, null);
766     }
767 
768     @Override
769     public void processDecodes(FacesContext context)
770     {
771         checkNull(context, "context");
772         _process(context, PhaseId.APPLY_REQUEST_VALUES, APPLY_REQUEST_VALUES_PROCESSOR);
773     }
774 
775     /**
776      * @since 2.0
777      */
778     @Override
779     public void processRestoreState(FacesContext context, Object state)
780     {
781         // The default implementation must call UIComponentBase.processRestoreState(javax.faces.context.FacesContext,
782         // java.lang.Object) from within a try block.
783         try
784         {
785             super.processRestoreState(context, state);
786         }
787         finally
788         {
789             // The try block must have a finally block that ensures that no FacesEvents remain in the event queue
790             broadcastEvents(context, PhaseId.RESTORE_VIEW);
791 
792             //visitTree(VisitContext.createVisitContext(context), new RestoreStateCallback());
793         }
794     }
795 
796     @Override
797     public void queueEvent(FacesEvent event)
798     {
799         checkNull(event, "event");
800         if (_events == null)
801         {
802             _events = new ArrayList<FacesEvent>();
803         }
804 
805         _events.add(event);
806     }
807 
808     @Override
809     public void processValidators(FacesContext context)
810     {
811         checkNull(context, "context");
812         _process(context, PhaseId.PROCESS_VALIDATIONS, PROCESS_VALIDATORS_PROCESSOR);
813     }
814 
815     @Override
816     public void processUpdates(FacesContext context)
817     {
818         checkNull(context, "context");
819         _process(context, PhaseId.UPDATE_MODEL_VALUES, UPDATE_MODEL_PROCESSOR);
820     }
821 
822     public void setLocale(Locale locale)
823     {
824         getStateHelper().put(PropertyKeys.locale, locale );
825     }
826 
827     /**
828      * Invoke view-specific phase listeners, plus an optional EL MethodExpression.
829      * <p>
830      * JSF1.2 adds the ability for PhaseListener objects to be added to a UIViewRoot instance, and for
831      * "beforePhaseListener" and "afterPhaseListener" EL expressions to be defined on the viewroot. This method is
832      * expected to be called at appropriate times, and will then execute the relevant listener callbacks.
833      * <p>
834      * Parameter "listener" may be null. If not null, then it is an EL expression pointing to a user method that will be
835      * invoked.
836      * <p>
837      * Note that the global PhaseListeners are invoked via the Lifecycle implementation, not from this method here.
838      * <p>
839      * These PhaseListeners are processed with the same rules as the globally defined PhaseListeners, except
840      * that any Exceptions, which may occur during the execution of the PhaseListeners, will only be logged
841      * and not published to the ExceptionHandler.
842      */
843     private boolean notifyListeners(FacesContext context, PhaseId phaseId, MethodExpression listener,
844                                     boolean beforePhase)
845     {
846         List<PhaseListener> phaseListeners = (List<PhaseListener>) getStateHelper().get(PropertyKeys.phaseListeners);
847         if (listener != null || (phaseListeners != null && !phaseListeners.isEmpty()))
848         {
849             // how many listeners do we have? (the MethodExpression listener is counted in either way)
850             // NOTE: beforePhaseSuccess[0] always refers to the MethodExpression listener
851             int listenerCount = (phaseListeners != null ? phaseListeners.size() + 1 : 1);
852             
853             boolean[] beforePhaseSuccess;
854             if (beforePhase)
855             {
856                 beforePhaseSuccess = new boolean[listenerCount];
857                 _getListenerSuccessMap().put(phaseId, beforePhaseSuccess);
858             }
859             else
860             {
861                 // afterPhase - get beforePhaseSuccess from the Map
862                 beforePhaseSuccess = _getListenerSuccessMap().get(phaseId);
863                 if (beforePhaseSuccess == null)
864                 {
865                     // no Map available - assume that everything went well
866                     beforePhaseSuccess = new boolean[listenerCount];
867                     Arrays.fill(beforePhaseSuccess, true);
868                 }
869             }
870             
871             PhaseEvent event = createEvent(context, phaseId);
872 
873             // only invoke the listener if we are in beforePhase
874             // or if the related before PhaseListener finished without an Exception
875             if (listener != null && (beforePhase || beforePhaseSuccess[0]))
876             {
877                 try
878                 {
879                     listener.invoke(context.getELContext(), new Object[] { event });
880                     beforePhaseSuccess[0] = true;
881                 }
882                 catch (Throwable t) 
883                 {
884                     beforePhaseSuccess[0] = false; // redundant - for clarity
885                     _getLogger().log(Level.SEVERE, "An Exception occured while processing " +
886                                              listener.getExpressionString() + 
887                                              " in Phase " + phaseId, t);
888                     if (beforePhase)
889                     {
890                         return context.getResponseComplete() ||
891                                 (context.getRenderResponse() && !PhaseId.RENDER_RESPONSE.equals(phaseId));
892                     }
893                 }
894             }
895             else if (beforePhase)
896             {
897                 // there is no beforePhase MethodExpression listener
898                 beforePhaseSuccess[0] = true;
899             }
900 
901             if (phaseListeners != null && !phaseListeners.isEmpty())
902             {
903                 if (beforePhase)
904                 {
905                     // process listeners in ascending order
906                     for (int i = 0; i < beforePhaseSuccess.length - 1; i++)
907                     {
908                         PhaseListener phaseListener;
909                         try 
910                         {
911                             phaseListener = phaseListeners.get(i);
912                         }
913                         catch (IndexOutOfBoundsException e)
914                         {
915                             // happens when a PhaseListener removes another PhaseListener 
916                             // from UIViewRoot in its beforePhase method
917                             throw new IllegalStateException("A PhaseListener must not remove " +
918                                     "PhaseListeners from UIViewRoot.");
919                         }
920                         PhaseId listenerPhaseId = phaseListener.getPhaseId();
921                         if (phaseId.equals(listenerPhaseId) || PhaseId.ANY_PHASE.equals(listenerPhaseId))
922                         {
923                             try
924                             {
925                                 phaseListener.beforePhase(event);
926                                 beforePhaseSuccess[i + 1] = true;
927                             }
928                             catch (Throwable t) 
929                             {
930                                 beforePhaseSuccess[i + 1] = false; // redundant - for clarity
931                                 _getLogger().log(Level.SEVERE, "An Exception occured while processing the " +
932                                                          "beforePhase method of PhaseListener " + phaseListener +
933                                                          " in Phase " + phaseId, t);
934                                 return context.getResponseComplete() ||
935                                         (context.getRenderResponse() && !PhaseId.RENDER_RESPONSE.equals(phaseId));
936                             }
937                         }
938                     }
939                 }
940                 else
941                 {
942                     // afterPhase
943                     // process listeners in descending order
944                     for (int i = beforePhaseSuccess.length - 1; i > 0; i--)
945                     {
946                         PhaseListener phaseListener;
947                         try 
948                         {
949                             phaseListener = phaseListeners.get(i - 1);
950                         }
951                         catch (IndexOutOfBoundsException e)
952                         {
953                             // happens when a PhaseListener removes another PhaseListener 
954                             // from UIViewRoot in its beforePhase or afterPhase method
955                             throw new IllegalStateException("A PhaseListener must not remove " +
956                                     "PhaseListeners from UIViewRoot.");
957                         }
958                         PhaseId listenerPhaseId = phaseListener.getPhaseId();
959                         if ((phaseId.equals(listenerPhaseId) || PhaseId.ANY_PHASE.equals(listenerPhaseId))
960                                 && beforePhaseSuccess[i])
961                         {
962                             try
963                             {
964                                 phaseListener.afterPhase(event);
965                             }
966                             catch (Throwable t) 
967                             {
968                                 logger.log(Level.SEVERE, "An Exception occured while processing the " +
969                                                          "afterPhase method of PhaseListener " + phaseListener +
970                                                          " in Phase " + phaseId, t);
971                             }
972                         }
973                     }
974                 }
975             }
976         }
977 
978         if (beforePhase)
979         {
980             return context.getResponseComplete() ||
981                     (context.getRenderResponse() && !PhaseId.RENDER_RESPONSE.equals(phaseId));
982         }
983         else
984         {
985             return context.getResponseComplete() || context.getRenderResponse();
986         }
987     }
988 
989     private PhaseEvent createEvent(FacesContext context, PhaseId phaseId)
990     {
991         if (_lifecycle == null)
992         {
993             LifecycleFactory factory = (LifecycleFactory)FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
994             String id = context.getExternalContext().getInitParameter(FacesServlet.LIFECYCLE_ID_ATTR);
995             if (id == null)
996             {
997                 id = LifecycleFactory.DEFAULT_LIFECYCLE;
998             }
999             _lifecycle = factory.getLifecycle(id);
1000         }
1001         return new PhaseEvent(context, phaseId, _lifecycle);
1002     }
1003 
1004     /**
1005      * Broadcast all events in the specified collection, stopping the at any time an AbortProcessingException
1006      * is thrown.
1007      *
1008      * @param context the current JSF context
1009      * @param events the events to broadcast
1010      * @return 
1011      *
1012      * @return <code>true</code> if the broadcast was completed without unexpected abortion/exception,
1013      *  <code>false</code> otherwise
1014      */
1015     private boolean _broadcastAll(FacesContext context,
1016                                List<? extends FacesEvent> events,
1017                                Collection<FacesEvent> eventsAborted)
1018     {
1019         assert events != null;
1020 
1021         for (int i = 0; i < events.size(); i++)
1022         {
1023             FacesEvent event = events.get(i);
1024             UIComponent source = event.getComponent();
1025             UIComponent compositeParent = UIComponent.getCompositeComponentParent(source);
1026             if (compositeParent != null)
1027             {
1028                 pushComponentToEL(context, compositeParent);
1029             }
1030             // Push the source as the current component
1031             pushComponentToEL(context, source);
1032 
1033             try
1034             {
1035                 // Actual event broadcasting
1036                 if (!source.isCachedFacesContext())
1037                 {
1038                     try
1039                     {
1040                         source.setCachedFacesContext(context);
1041                         source.broadcast(event);
1042                     }
1043                     finally
1044                     {
1045                         source.setCachedFacesContext(null);
1046                     }
1047                 }
1048                 else
1049                 {
1050                     source.broadcast(event);
1051                 }
1052             }
1053             catch (Exception e)
1054             {
1055 
1056                 Throwable cause = e;
1057                 AbortProcessingException ape = null;
1058                 do
1059                 {
1060                     if (cause != null && cause instanceof AbortProcessingException)
1061                     {
1062                         ape = (AbortProcessingException) cause;
1063                         break;
1064                     }
1065                     cause = cause.getCause();
1066                 }
1067                 while (cause != null);
1068                 
1069                 // for any other exception publish ExceptionQueuedEvent
1070                 // publish the Exception to be handled by the ExceptionHandler
1071                 // to publish or to not publish APE? That is the question : MYFACES-3199. We publish it,
1072                 // because user can handle it in custom exception handler then. 
1073                 if (ape != null)
1074                 {
1075                     e = ape;
1076                 }
1077                 ExceptionQueuedEventContext exceptionContext 
1078                         = new ExceptionQueuedEventContext(context, e, source, context.getCurrentPhaseId());
1079                 context.getApplication().publishEvent(context, ExceptionQueuedEvent.class, exceptionContext);
1080 
1081                 
1082                 if (ape != null)
1083                 {
1084                     // APE found,  abortion for this event only
1085                     eventsAborted.add(event);
1086                 }
1087                 else
1088                 {
1089                     // We can't continue broadcast processing if other exception is thrown:
1090                     return false;
1091                 }
1092             }
1093             finally
1094             {
1095                 // Restore the current component
1096                 source.popComponentFromEL(context);
1097                 if (compositeParent != null)
1098                 {
1099                     compositeParent.popComponentFromEL(context);
1100                 }
1101             }
1102         }
1103         return true;
1104     }
1105 
1106     private void clearEvents()
1107     {
1108         _events = null;
1109     }
1110 
1111     private void checkNull(Object value, String valueLabel)
1112     {
1113         if (value == null)
1114         {
1115             throw new NullPointerException(valueLabel + " is null");
1116         }
1117     }
1118 
1119     public void setRenderKitId(String renderKitId)
1120     {
1121         getStateHelper().put(PropertyKeys.renderKitId, renderKitId );
1122     }
1123 
1124     /**
1125      * DO NOT USE.
1126      * <p>
1127      * This inherited property is disabled. Although this class extends a base-class that defines a read/write rendered
1128      * property, this particular subclass does not support setting it. Yes, this is broken OO design: direct all
1129      * complaints to the JSF spec group.
1130      */
1131     @Override
1132     @JSFProperty(tagExcluded = true)
1133     public void setRendered(boolean state)
1134     {
1135         // Call parent method due to TCK problems
1136         super.setRendered(state);
1137         // throw new UnsupportedOperationException();
1138     }
1139 
1140     /**
1141      * DO NOT USE.
1142      * <p>
1143      * Although this class extends a base-class that defines a read/write id property, it makes no sense for this
1144      * particular subclass to support it. The tag library does not export this property for use, but there is no way to
1145      * "undeclare" a java method. Yes, this is broken OO design: direct all complaints to the JSF spec group.
1146      * <p>
1147      * This property should be disabled (ie throw an exception if invoked). However there are currently several places
1148      * that call this method (eg during restoreState) so it just does the normal thing for the moment. TODO: fix callers
1149      * then make this throw an exception.
1150      *
1151      * @JSFProperty tagExcluded="true"
1152      */
1153     @Override
1154     public void setId(String id)
1155     {
1156         // throw new UnsupportedOperationException();
1157 
1158         // Leave enabled for now. Things like the TreeStructureManager call this,
1159         // even though they probably should not.
1160         super.setId(id);
1161     }
1162     
1163     /**
1164      * {@inheritDoc}
1165      */
1166     @Override
1167     public void setInView(boolean isInView)
1168     {
1169         // no-op view root is always in view
1170     }
1171 
1172     public void removeComponentResource(FacesContext context, UIComponent componentResource)
1173     {
1174         removeComponentResource(context, componentResource, null);
1175     }
1176 
1177     public void removeComponentResource(FacesContext context, UIComponent componentResource, String target)
1178     {
1179         // If the target argument is null
1180         if (target == null)
1181         {
1182             // Look for a target attribute on the component
1183             target = (String)componentResource.getAttributes().get("target");
1184 
1185             // If there is no target attribute
1186             if (target == null)
1187             {
1188                 // Set target to be the default value head
1189                 target = "head";
1190             }
1191         }
1192 
1193 
1194         // Call getComponentResources to obtain the child list for the given target.
1195         //List<UIComponent> componentResources = getComponentResources(context, target);
1196         UIComponent facet = getFacet(target);
1197         if (facet != null)
1198         {
1199             //Only if the facet is found it is possible to remove the resource,
1200             //otherwise nothing should happen (call to getComponentResource trigger
1201             //creation of facet)
1202             // Remove the component resource from the child list
1203             facet.getChildren().remove(componentResource);
1204         }
1205     }
1206 
1207     public void setViewId(String viewId)
1208     {
1209         // It really doesn't make much sense to allow null here.
1210         // However the TCK does not check for it, and sun's implementation
1211         // allows it so here we allow it too.
1212         getStateHelper().put(PropertyKeys.viewId, viewId );
1213     }
1214 
1215     /**
1216      * Removes a The phaseListeners attached to ViewRoot.
1217      */
1218     public void removePhaseListener(PhaseListener phaseListener)
1219     {
1220         if (phaseListener == null)
1221         {
1222             return;
1223         }
1224 
1225         getStateHelper().remove(PropertyKeys.phaseListeners, phaseListener);
1226     }
1227 
1228     /**
1229      * Sets
1230      *
1231      * @param beforePhaseListener
1232      *            the new beforePhaseListener value
1233      */
1234     public void setBeforePhaseListener(MethodExpression beforePhaseListener)
1235     {
1236         getStateHelper().put(PropertyKeys.beforePhaseListener, beforePhaseListener);
1237     }
1238 
1239     /**
1240      * Sets
1241      *
1242      * @param afterPhaseListener
1243      *            the new afterPhaseListener value
1244      */
1245     public void setAfterPhaseListener(MethodExpression afterPhaseListener)
1246     {
1247         getStateHelper().put(PropertyKeys.afterPhaseListener, afterPhaseListener);
1248     }
1249     
1250     @Override
1251     public Map<String, Object> getAttributes()
1252     {
1253         if (_attributesMap == null)
1254         {
1255             _attributesMap = new _ViewAttributeMap(this, super.getAttributes());
1256         }
1257         return _attributesMap;
1258     }
1259 
1260     /**
1261      * Indicates if the component is created when facelets builds the view and
1262      * is caused by the presence of a ResourceDependency annotation.
1263      * 
1264      * @return the _resourceDependencyUniqueId
1265      */
1266     boolean isResourceDependencyUniqueId()
1267     {
1268         return _resourceDependencyUniqueId;
1269     }
1270 
1271     void setResourceDependencyUniqueId(boolean resourceDependencyUniqueId)
1272     {
1273         this._resourceDependencyUniqueId = resourceDependencyUniqueId;
1274     }
1275     
1276     enum PropertyKeys
1277     {
1278          afterPhaseListener
1279         , beforePhaseListener
1280         , phaseListeners
1281         , locale
1282         , renderKitId
1283         , viewId
1284         , uniqueIdCounter
1285         , resourceDependencyUniqueIdCounter
1286     }
1287     
1288     @Override
1289     public Object saveState(FacesContext facesContext)
1290     {
1291         if (initialStateMarked())
1292         {
1293             Object parentSaved = super.saveState(facesContext);
1294             if (parentSaved == null && _viewScope == null)
1295             {
1296                 //No values
1297                 return null;
1298             }
1299             else if (parentSaved == null && _viewScope != null && _viewScope.size() == 0)
1300             {
1301                 //Empty view scope, no values
1302                 return null;
1303             }
1304             
1305             Object[] values = new Object[2];
1306             values[0] = parentSaved;
1307             values[1] = saveAttachedState(facesContext,_viewScope);
1308             return values;
1309         }
1310         else
1311         {
1312             Object[] values = new Object[2];
1313             values[0] = super.saveState(facesContext);
1314             values[1] = saveAttachedState(facesContext,_viewScope);
1315             return values;
1316         }
1317     }
1318 
1319     @SuppressWarnings("unchecked")
1320     @Override
1321     public void restoreState(FacesContext facesContext, Object state)
1322     {
1323         if (state == null)
1324         {
1325             return;
1326         }
1327         
1328         Object[] values = (Object[])state;
1329         super.restoreState(facesContext,values[0]);
1330         _viewScope = (Map<String, Object>) restoreAttachedState(facesContext, values[1]);
1331     }
1332     
1333     public List<SystemEventListener> getViewListenersForEventClass(Class<? extends SystemEvent> systemEvent)
1334     {
1335         checkNull (systemEvent, "systemEvent");
1336         if (_systemEventListeners == null)
1337         {
1338             return null;
1339         }
1340         return _systemEventListeners.get (systemEvent);
1341     }
1342     
1343     public void subscribeToViewEvent(Class<? extends SystemEvent> systemEvent,
1344             SystemEventListener listener)
1345     {
1346         List<SystemEventListener> listeners;
1347         
1348         checkNull (systemEvent, "systemEvent");
1349         checkNull (listener, "listener");
1350         
1351         if (_systemEventListeners == null)
1352         {
1353             _systemEventListeners = new HashMap<Class<? extends SystemEvent>, List<SystemEventListener>>();
1354         }
1355         
1356         listeners = _systemEventListeners.get (systemEvent);
1357         
1358         if (listeners == null)
1359         {
1360             listeners = new ArrayList<SystemEventListener>();
1361             
1362             _systemEventListeners.put (systemEvent, listeners);
1363         }
1364         
1365         listeners.add (listener);
1366     }
1367     
1368     public void unsubscribeFromViewEvent(Class<? extends SystemEvent> systemEvent,
1369             SystemEventListener listener)
1370     {
1371         List<SystemEventListener> listeners;
1372         
1373         checkNull (systemEvent, "systemEvent");
1374         checkNull (listener, "listener");
1375         
1376         if (_systemEventListeners == null)
1377         {
1378             return;
1379         }
1380         
1381         listeners = _systemEventListeners.get (systemEvent);
1382         
1383         if (listeners != null)
1384         {
1385             listeners.remove (listener);
1386         }
1387     }
1388 
1389     /**
1390      * Process the specified phase by calling PhaseListener.beforePhase for every phase listeners defined on this
1391      * view root, then calling the process method of the processor, broadcasting relevant events and finally
1392      * notifying the afterPhase method of every phase listeners registered on this view root.
1393      *
1394      * @param context
1395      * @param phaseId
1396      * @param processor
1397      * @param broadcast
1398      *
1399      * @return
1400      */
1401     private boolean _process(FacesContext context, PhaseId phaseId, PhaseProcessor processor)
1402     {
1403         RuntimeException processingException = null;
1404         try
1405         {
1406             if (!notifyListeners(context, phaseId, getBeforePhaseListener(), true))
1407             {
1408                 try
1409                 {
1410                     if (processor != null)
1411                     {
1412                         processor.process(context, this);
1413                     }
1414         
1415                     broadcastEvents(context, phaseId);
1416                 }
1417                 catch (RuntimeException re)
1418                 {
1419                     // catch any Exception that occures while processing the phase
1420                     // to ensure invocation of the afterPhase methods
1421                     processingException = re;
1422                 }
1423             }
1424         }
1425         finally
1426         {
1427             if (context.getRenderResponse() || context.getResponseComplete())
1428             {
1429                 clearEvents();
1430             }            
1431         }
1432 
1433         boolean retVal = notifyListeners(context, phaseId, getAfterPhaseListener(), false);
1434         if (processingException == null) 
1435         {
1436             return retVal;   
1437         }
1438         else
1439         {
1440             throw processingException;
1441         }
1442     }
1443 
1444     private void _processDecodesDefault(FacesContext context)
1445     {
1446         super.processDecodes(context);
1447     }
1448 
1449     private void _processUpdatesDefault(FacesContext context)
1450     {
1451         super.processUpdates(context);
1452     }
1453 
1454     private void _processValidatorsDefault(FacesContext context)
1455     {
1456         super.processValidators(context);
1457     }
1458 
1459     /**
1460      * Gathers all event for current and ANY phase
1461      * @param phaseId current phase id
1462      */
1463     private Events _getEvents(PhaseId phaseId)
1464     {
1465         // Gather the events and purge the event list to prevent concurrent modification during broadcasting
1466         int size = _events.size();
1467         List<FacesEvent> anyPhase = new ArrayList<FacesEvent>(size);
1468         List<FacesEvent> onPhase = new ArrayList<FacesEvent>(size);
1469         
1470         for (int i = 0; i < size; i++)
1471         {
1472             FacesEvent event = _events.get(i);
1473             if (event.getPhaseId().equals(PhaseId.ANY_PHASE))
1474             {
1475                 anyPhase.add(event);
1476                 _events.remove(i);
1477                 size--;
1478                 i--;
1479             }
1480             else if (event.getPhaseId().equals(phaseId))
1481             {
1482                 onPhase.add(event);
1483                 _events.remove(i);
1484                 size--;
1485                 i--;
1486             }
1487         }
1488         
1489         return new Events(anyPhase, onPhase);
1490     }
1491     
1492     private Logger _getLogger()
1493     {
1494         if (logger == null)
1495         {
1496             logger = Logger.getLogger(UIViewRoot.class.getName());
1497         }
1498         return logger;
1499     }
1500 
1501     private Map<PhaseId, boolean[]> _getListenerSuccessMap()
1502     {
1503         // lazy init: 
1504         if (listenerSuccessMap == null)
1505         {
1506             listenerSuccessMap = new HashMap<PhaseId, boolean[]>();
1507         }
1508         return listenerSuccessMap;
1509     }
1510 
1511     private static interface PhaseProcessor
1512     {
1513         public void process(FacesContext context, UIViewRoot root);
1514     }
1515 
1516     private static class ApplyRequestValuesPhaseProcessor implements PhaseProcessor
1517     {
1518         public void process(FacesContext context, UIViewRoot root)
1519         {
1520             PartialViewContext pvc = context.getPartialViewContext();
1521             // Perform partial processing by calling PartialViewContext.processPartial(javax.faces.event.PhaseId)
1522             // with PhaseId.UPDATE_MODEL_VALUES if:
1523             //   * PartialViewContext.isPartialRequest() returns true and we don't have a request to process all
1524             // components in the view (PartialViewContext.isExecuteAll() returns false)
1525             //section 13.4.2 from the  JSF2  spec also see https://issues.apache.org/jira/browse/MYFACES-2119
1526             if (pvc.isPartialRequest() && !pvc.isExecuteAll())
1527             {
1528                 pvc.processPartial(PhaseId.APPLY_REQUEST_VALUES);
1529             }
1530             // Perform full processing by calling UIComponentBase.processUpdates(javax.faces.context.FacesContext)
1531             // if one of the following conditions are met:
1532             // *   PartialViewContext.isPartialRequest() returns true and we have a request to process all components
1533             // in the view (PartialViewContext.isExecuteAll() returns true)
1534             // *   PartialViewContext.isPartialRequest() returns false
1535             else
1536             {
1537                 root._processDecodesDefault(context);
1538             }
1539         }
1540     }
1541 
1542     private static class ProcessValidatorPhaseProcessor implements PhaseProcessor
1543     {
1544         public void process(FacesContext context, UIViewRoot root)
1545         {
1546             PartialViewContext pvc = context.getPartialViewContext();
1547             // Perform partial processing by calling PartialViewContext.processPartial(javax.faces.event.PhaseId)
1548             // with PhaseId.UPDATE_MODEL_VALUES if:
1549             // PartialViewContext.isPartialRequest() returns true and we don't have a request to process all components
1550             // in the view (PartialViewContext.isExecuteAll() returns false)
1551             //section 13.4.2 from the  JSF2  spec also see https://issues.apache.org/jira/browse/MYFACES-2119
1552             if (pvc.isPartialRequest() && !pvc.isExecuteAll())
1553             {
1554                 pvc.processPartial(PhaseId.PROCESS_VALIDATIONS);
1555             }
1556             // Perform full processing by calling UIComponentBase.processUpdates(javax.faces.context.FacesContext)
1557             // if one of the following conditions are met:
1558             // *   PartialViewContext.isPartialRequest() returns true and we have a request to process all components
1559             // in the view (PartialViewContext.isExecuteAll() returns true)
1560             // *   PartialViewContext.isPartialRequest() returns false
1561             else
1562             {
1563                 root._processValidatorsDefault(context);
1564             }
1565         }
1566     }
1567 
1568     private static class UpdateModelPhaseProcessor implements PhaseProcessor
1569     {
1570         public void process(FacesContext context, UIViewRoot root)
1571         {
1572             PartialViewContext pvc = context.getPartialViewContext();
1573             // Perform partial processing by calling PartialViewContext.processPartial(javax.faces.event.PhaseId)
1574             // with PhaseId.UPDATE_MODEL_VALUES if:
1575             //   * PartialViewContext.isPartialRequest() returns true and we don't have a request to process
1576             // all components in the view (PartialViewContext.isExecuteAll() returns false)
1577             //section 13.4.2 from the JSF2 spec also see https://issues.apache.org/jira/browse/MYFACES-2119
1578             if (pvc.isPartialRequest() && !pvc.isExecuteAll())
1579             {
1580                 pvc.processPartial(PhaseId.UPDATE_MODEL_VALUES);
1581             }
1582             // Perform full processing by calling UIComponentBase.processUpdates(javax.faces.context.FacesContext)
1583             // if one of the following conditions are met:
1584             // *   PartialViewContext.isPartialRequest() returns true and we have a request to process all components
1585             // in the view (PartialViewContext.isExecuteAll() returns true)
1586             // *   PartialViewContext.isPartialRequest() returns false
1587             else
1588             {
1589                 root._processUpdatesDefault(context);
1590             }
1591         }
1592     }
1593 
1594 /*
1595     private static class RestoreStateCallback implements VisitCallback
1596     {
1597         private PostRestoreStateEvent event;
1598 
1599         public VisitResult visit(VisitContext context, UIComponent target)
1600         {
1601             if (event == null)
1602             {
1603                 event = new PostRestoreStateEvent(target);
1604             }
1605             else
1606             {
1607                 event.setComponent(target);
1608             }
1609 
1610             // call the processEvent method of the current component.
1611             // The argument event must be an instance of AfterRestoreStateEvent whose component
1612             // property is the current component in the traversal.
1613             target.processEvent(event);
1614             
1615             return VisitResult.ACCEPT;
1616         }
1617     }
1618 */
1619 
1620     // we cannot make this class a inner class, because the 
1621     // enclosing class (UIViewRoot) would also have to be serialized.
1622     private static class ViewScope extends HashMap<String, Object>
1623     {
1624         
1625         private static final long serialVersionUID = -1088893802269478164L;
1626         
1627         @Override
1628         public void clear()
1629         {
1630             /*
1631              * The returned Map must be implemented such that calling clear() on the Map causes
1632              * Application.publishEvent(java.lang.Class, java.lang.Object) to be called, passing
1633              * ViewMapDestroyedEvent.class as the first argument and this UIViewRoot instance as the second argument.
1634              */
1635             FacesContext facesContext = FacesContext.getCurrentInstance();
1636             facesContext.getApplication().publishEvent(facesContext, 
1637                     PreDestroyViewMapEvent.class, facesContext.getViewRoot());
1638             
1639             super.clear();
1640         }
1641         
1642     }
1643 
1644     /**
1645      * Agregates events for ANY_PHASE and current phase 
1646      */
1647     private class Events
1648     {
1649         
1650         private final List<FacesEvent> _anyPhase;
1651         
1652         private final List<FacesEvent> _onPhase;
1653         
1654         public Events(List<FacesEvent> anyPhase, List<FacesEvent> onPhase)
1655         {
1656             super();
1657             this._anyPhase = anyPhase;
1658             this._onPhase = onPhase;
1659         }
1660 
1661         public boolean hasMoreEvents()
1662         {
1663             return (_anyPhase != null && _anyPhase.size() > 0) || (_onPhase != null && _onPhase.size() > 0); 
1664         }
1665 
1666         public List<FacesEvent> getAnyPhase()
1667         {
1668             return _anyPhase;
1669         }
1670 
1671         public List<FacesEvent> getOnPhase()
1672         {
1673             return _onPhase;
1674         }
1675     }
1676 }