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