View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.view.facelets;
20  
21  import java.io.Serializable;
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.EnumSet;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  import javax.el.ValueExpression;
31  import javax.faces.FacesException;
32  import javax.faces.FactoryFinder;
33  import javax.faces.application.ProjectStage;
34  import javax.faces.application.StateManager;
35  import javax.faces.component.ContextCallback;
36  import javax.faces.component.UIComponent;
37  import javax.faces.component.UIComponentBase;
38  import javax.faces.component.UIViewRoot;
39  import javax.faces.component.visit.VisitCallback;
40  import javax.faces.component.visit.VisitContext;
41  import javax.faces.component.visit.VisitContextFactory;
42  import javax.faces.component.visit.VisitHint;
43  import javax.faces.component.visit.VisitResult;
44  import javax.faces.context.FacesContext;
45  import javax.faces.event.PostAddToViewEvent;
46  import javax.faces.event.PreRemoveFromViewEvent;
47  import javax.faces.event.SystemEvent;
48  import javax.faces.event.SystemEventListener;
49  import javax.faces.render.RenderKitFactory;
50  import javax.faces.render.ResponseStateManager;
51  import javax.faces.view.StateManagementStrategy;
52  import javax.faces.view.ViewDeclarationLanguage;
53  import javax.faces.view.ViewDeclarationLanguageFactory;
54  import javax.faces.view.ViewMetadata;
55  import org.apache.myfaces.application.StateManagerImpl;
56  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
57  import org.apache.myfaces.context.RequestViewContext;
58  import org.apache.myfaces.shared.config.MyfacesConfig;
59  import org.apache.myfaces.shared.util.ClassUtils;
60  import org.apache.myfaces.shared.util.HashMapUtils;
61  import org.apache.myfaces.shared.util.WebConfigParamUtils;
62  import org.apache.myfaces.view.facelets.compiler.CheckDuplicateIdFaceletUtils;
63  import org.apache.myfaces.view.facelets.pool.ViewEntry;
64  import org.apache.myfaces.view.facelets.pool.ViewPool;
65  import org.apache.myfaces.view.facelets.pool.ViewStructureMetadata;
66  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
67  import org.apache.myfaces.view.facelets.tag.jsf.FaceletState;
68  
69  /**
70   * This class implements partial state saving feature when facelets
71   * is used to render pages. (Theorically it could be applied on jsp case too,
72   * but all considerations below should be true before apply it).
73   * 
74   * The following considerations apply for this class:
75   * 
76   * 1. This StateManagementStrategy should only be active if javax.faces.PARTIAL_STATE_SAVING
77   *    config param is active(true). See javadoc on StateManager for details.
78   * 2. A map using component clientId as keys are used to hold the state.
79   * 3. Each component has a valid id after ViewDeclarationLanguage.buildView().
80   *    This implies that somewhere, every TagHandler that create an UIComponent 
81   *    instance should call setId and assign it.
82   * 4. Every TagHandler that create an UIComponent instance should call markInitialState
83   *    after the component is populated. Otherwise, full state is always saved.
84   * 5. A SystemEventListener is used to keep track for added and removed components, listen
85   *    PostAddToViewEvent and PreRemoveFromViewEvent event triggered by UIComponent.setParent()
86   *    method.
87   * 6. It is not possible to use javax.faces.component.visit API to traverse the component
88   *    tree during save/restore, because UIData.visitTree traverse all rows and we only need
89   *    to restore state per component (not per row).
90   * 7. It is necessary to preserve the order of the children added/removed between requests.
91   * 8. Added and removed components could be seen as subtrees. This imply that we need to save
92   *    the structure of the added components subtree and remove one component could be remove
93   *    all its children and facets from view inclusive.
94   * 9. It is necessary to save and restore the list of added/removed components between several
95   *    requests.
96   * 10.All components ids removed in any moment of time must be preserved.
97   * 11.Each component must be restored only once.
98   * 11.The order is important for ids added when it is traversed the tree, otherwise the algorithm 
99   *    could change the order in which components will be restored.  
100  * 
101  * @author Leonardo Uribe (latest modification by $Author$)
102  * @version $Revision$ $Date$
103  * @since 2.0
104  *
105  */
106 public class DefaultFaceletsStateManagementStrategy extends StateManagementStrategy
107 {
108     public static final String CLIENTIDS_ADDED = "oam.CLIENTIDS_ADDED";
109     
110     public static final String CLIENTIDS_REMOVED = "oam.CLIENTIDS_REMOVED";
111     
112     /**
113      * Key used on component attribute map to indicate if a component was added
114      * after build view, so itself and all descendants should not use partial
115      * state saving. There are two possible values:
116      * 
117      * Key not present: The component uses pss.
118      * ComponentState.ADD: The component was added to the view after build view.
119      * ComponentState.REMOVE_ADD: The component was removed/added to the view. Itself and all
120      * descendants should be saved and restored, but we have to unregister/register
121      * from CLIENTIDS_ADDED and CLIENTIDS_REMOVED lists. See ComponentSupport.markComponentToRestoreFully
122      * for details.
123      * ComponentState.ADDED: The component has been added or removed/added, but it has
124      * been already processed.
125      */
126     public  static final String COMPONENT_ADDED_AFTER_BUILD_VIEW = "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW"; 
127     
128     /**
129      * If this param is set to true (by default), when pss algorithm is executed to save state, a visit tree
130      * traversal is done, instead a plain traversal like previous versions (2.0.7/2.1.1 and earlier) of MyFaces Core.
131      * 
132      * This param is just provided to preserve backwards behavior. 
133      * @deprecated JSF 2.2 change enforces tree visiting as the preferred way. Performance tests shows that plain
134      * visit is faster but the difference is negligible.
135      */
136     @Deprecated
137     @JSFWebConfigParam(since="2.0.8, 2.1.2", defaultValue="true", expectedValues="true, false",
138                        group="state", tags="performance", deprecated=true)
139     public static final String SAVE_STATE_WITH_VISIT_TREE_ON_PSS
140             = "org.apache.myfaces.SAVE_STATE_WITH_VISIT_TREE_ON_PSS";
141     
142     /**
143      * Define how duplicate ids are checked when ProjectStage is Production, by default (auto) it only check ids of
144      * components that does not encapsulate markup (like facelets UILeaf).
145      *  
146      * <ul>
147      * <li>true: check all ids, including ids for components that are transient and encapsulate markup.</li>
148      * <li>auto: (default) check ids of components that does not encapsulate markup (like facelets UILeaf). 
149      * Note ids of UILeaf instances are generated by facelets vdl, start with "j_id", are never rendered 
150      * into the response and UILeaf instances are never used as a target for listeners, so in practice 
151      * there is no need to check such ids. This reduce the overhead associated with generate client ids.</li>
152      * <li>false: do not do any check when ProjectStage is Production</li>
153      * </ul>
154      * <p> According to specification, identifiers must be unique within the scope of the nearest ancestor to 
155      * the component that is a naming container.</p>
156      */
157     @JSFWebConfigParam(since="2.0.12, 2.1.6", defaultValue="auto", expectedValues="true, auto, false",
158                        group="state", tags="performance")
159     public static final String CHECK_ID_PRODUCTION_MODE
160             = "org.apache.myfaces.CHECK_ID_PRODUCTION_MODE";
161     
162     private static final String CHECK_ID_PRODUCTION_MODE_DEFAULT = "auto";
163     private static final String CHECK_ID_PRODUCTION_MODE_TRUE = "true";
164     private static final String CHECK_ID_PRODUCTION_MODE_AUTO = "auto";
165     
166     private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
167     
168     private static final String SERIALIZED_VIEW_REQUEST_ATTR = 
169         StateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
170     
171     private static final Object[] EMPTY_STATES = new Object[]{null, null};
172     
173     private static final Set<VisitHint> VISIT_HINTS = Collections.unmodifiableSet( 
174             EnumSet.of(VisitHint.SKIP_ITERATION));
175     
176     private static final String UNIQUE_ID_COUNTER_KEY =
177               "oam.view.uniqueIdCounter";
178     
179     private ViewDeclarationLanguageFactory _vdlFactory;
180     
181     private RenderKitFactory _renderKitFactory = null;
182     
183     private VisitContextFactory _visitContextFactory = null;
184     
185     private String _checkIdsProductionMode;
186     
187     private MyfacesConfig _config;
188     
189     private ViewPoolProcessor _viewPoolProcessor;
190     
191     public DefaultFaceletsStateManagementStrategy ()
192     {
193         this(FacesContext.getCurrentInstance());
194     }
195     
196     public DefaultFaceletsStateManagementStrategy (FacesContext context)
197     {
198         _vdlFactory = (ViewDeclarationLanguageFactory)
199                 FactoryFinder.getFactory(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY);
200         _config = MyfacesConfig.getCurrentInstance(context.getExternalContext());
201         _viewPoolProcessor = ViewPoolProcessor.getInstance(context);
202     }
203     
204     @SuppressWarnings("unchecked")
205     @Override
206     public UIViewRoot restoreView (FacesContext context, String viewId, String renderKitId)
207     {
208         ResponseStateManager manager;
209         Object state[];
210         Map<String, Object> states;
211         UIViewRoot view = null;
212  
213         // The value returned here is expected to be false (set by RestoreViewExecutor), but
214         //we don't know if some ViewHandler wrapper could change it, so it is better to save the value.
215         final boolean oldContextEventState = context.isProcessingEvents();
216         // Get previous state from ResponseStateManager.
217         manager = getRenderKitFactory().getRenderKit(context, renderKitId).getResponseStateManager();
218         
219         state = (Object[]) manager.getState(context, viewId);
220         
221         if (state == null)
222         {
223             //No state could be restored, return null causing ViewExpiredException
224             return null;
225         }
226         
227         if (state[1] instanceof Object[])
228         {
229             Object[] fullState = (Object[]) state[1]; 
230             view = (UIViewRoot) internalRestoreTreeStructure((TreeStructComponent)fullState[0]);
231 
232             if (view != null)
233             {
234                 context.setViewRoot (view);
235                 view.processRestoreState(context, fullState[1]);
236                 
237                 // If the view is restored fully, it is necessary to refresh RequestViewContext, otherwise at
238                 // each ajax request new components associated with @ResourceDependency annotation will be added
239                 // to the tree, making the state bigger without real need.
240                 RequestViewContext.getCurrentInstance(context).
241                         refreshRequestViewContext(context, view);
242                 
243                 if (fullState.length == 3 && fullState[2] != null)
244                 {
245                     context.setResourceLibraryContracts((List) UIComponentBase.
246                         restoreAttachedState(context, fullState[2]));
247                 }
248             }
249         }
250         else
251         {
252             // Per the spec: build the view.
253             ViewDeclarationLanguage vdl = _vdlFactory.getViewDeclarationLanguage(viewId);
254             Object faceletViewState = null;
255             try
256             {
257                 ViewMetadata metadata = vdl.getViewMetadata (context, viewId);
258                 
259                 if (metadata != null)
260                 {
261                     view = metadata.createMetadataView(context);
262                     
263                     // If no view and response complete there is no need to continue
264                     if (view == null && context.getResponseComplete())
265                     {
266                         return null;
267                     }
268                 }
269                 if (view == null)
270                 {
271                     view = context.getApplication().getViewHandler().createView(context, viewId);
272                 }
273                 
274                 context.setViewRoot (view); 
275                 boolean skipBuildView = false;
276                 if (state != null && state[1] != null)
277                 {
278                     // Since JSF 2.2, UIViewRoot.restoreViewScopeState() must be called, but
279                     // to get the state of the root, it is necessary to force calculate the
280                     // id from this location. Remember in this point, PSS is enabled, so the
281                     // code match with the assigment done in 
282                     // FaceletViewDeclarationLanguage.buildView()
283                     states = (Map<String, Object>) state[1];
284                     faceletViewState = UIComponentBase.restoreAttachedState(
285                             context,states.get(ComponentSupport.FACELET_STATE_INSTANCE));
286                     if (faceletViewState != null && _viewPoolProcessor != null)
287                     {
288                         ViewPool viewPool = _viewPoolProcessor.getViewPool(context, view);
289                         if (viewPool != null)
290                         {
291                             ViewStructureMetadata viewMetadata = viewPool.retrieveDynamicViewStructureMetadata(
292                                 context, view, (FaceletState) faceletViewState);
293                             if (viewMetadata != null)
294                             {
295                                 ViewEntry entry = viewPool.popDynamicStructureView(context, view,
296                                         (FaceletState) faceletViewState);
297                                 if (entry != null)
298                                 {
299                                     skipBuildView = true;
300                                     _viewPoolProcessor.cloneAndRestoreView(context, view, entry, viewMetadata);
301                                 }
302                             }
303                         }
304                     }
305                     if (view.getId() == null)
306                     {
307                         view.setId(view.createUniqueId(context, null));
308                     }
309                     if (faceletViewState != null)
310                     {
311                         //if (skipBuildView)
312                         //{
313                             FaceletState newFaceletState = (FaceletState) view.getAttributes().get(
314                                     ComponentSupport.FACELET_STATE_INSTANCE);
315                             if (newFaceletState != null)
316                             {
317                                 newFaceletState.restoreState(context, 
318                                         ((FaceletState)faceletViewState).saveState(context));
319                                 faceletViewState = newFaceletState;
320                             }
321                             else
322                             {
323                                 view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
324                             }
325                         //}
326                         //else
327                         //{
328                         //    view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
329                         //}
330                     }
331                     if (state.length == 3)
332                     {
333                         //Jump to where the count is
334                         view.getAttributes().put(UNIQUE_ID_COUNTER_KEY, state[2]);
335                     }
336                     Object viewRootState = states.get(view.getClientId(context));
337                     if (viewRootState != null)
338                     {
339                         try
340                         {
341                             view.pushComponentToEL(context, view);
342                             view.restoreViewScopeState(context, viewRootState);
343                         }
344                         finally
345                         {
346                             view.popComponentFromEL(context);
347                         }
348                     }
349                 }
350                 // On RestoreViewExecutor, setProcessingEvents is called first to false
351                 // and then to true when postback. Since we need listeners registered to PostAddToViewEvent
352                 // event to be handled, we should enable it again. For partial state saving we need this listeners
353                 // be called from here and relocate components properly.
354                 if (!skipBuildView)
355                 {
356                     try 
357                     {
358                         context.setProcessingEvents (true);
359                         vdl.buildView (context, view);
360                         // In the latest code related to PostAddToView, it is
361                         // triggered no matter if it is applied on postback. It seems that MYFACES-2389, 
362                         // TRINIDAD-1670 and TRINIDAD-1671 are related.
363                         suscribeListeners(view);
364                     }
365                     finally
366                     {
367                         context.setProcessingEvents (oldContextEventState);
368                     }
369                 }
370             }
371             catch (Throwable e)
372             {
373                 throw new FacesException ("unable to create view \"" + viewId + "\"", e);
374             }
375 
376             // Stateless mode only for transient views and non stateless mode for
377             // stateful views. This check avoid apply state over a stateless view.
378             boolean statelessMode = manager.isStateless(context, viewId);
379             if (statelessMode && !view.isTransient())
380             {
381                 throw new IllegalStateException("View is not transient");
382             }
383             if (!statelessMode && view.isTransient())
384             {
385                 throw new IllegalStateException("Cannot apply state over stateless view");
386             }
387             
388             if (state != null && state[1] != null)
389             {
390                 states = (Map<String, Object>) state[1];
391                 //Save the last unique id counter key in UIViewRoot
392                 Integer lastUniqueIdCounter = (Integer) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
393                 // Retrieve the facelet state before restore anything. The reason is
394                 // it could be necessary to restore the bindings map from here.
395                 FaceletState oldFaceletState = (FaceletState) view.getAttributes().get(
396                         ComponentSupport.FACELET_STATE_INSTANCE);
397                 
398                 // Visit the children and restore their state.
399                 boolean emptyState = false;
400                 boolean containsFaceletState = states.containsKey(
401                         ComponentSupport.FACELET_STATE_INSTANCE);
402                 if (states.isEmpty())
403                 {
404                     emptyState = true; 
405                 }
406                 else if (states.size() == 1 && 
407                         containsFaceletState)
408                 {
409                     emptyState = true; 
410                 }
411                 //Restore state of current components
412                 if (!emptyState)
413                 {
414                     // Check if there is only one component state
415                     // and that state is UIViewRoot instance (for example
416                     // when using ViewScope)
417                     if ((states.size() == 1 && !containsFaceletState) || 
418                         (states.size() == 2 && containsFaceletState))
419                     {
420                         Object viewState = states.get(view.getClientId(context));
421                         if (viewState != null)
422                         {
423                             restoreViewRootOnlyFromMap(context,viewState, view);
424                         }
425                         else
426                         {
427                             //The component is not viewRoot, restore as usual.
428                             restoreStateFromMap(context, states, view);
429                         }
430                     }
431                     else
432                     {
433                         restoreStateFromMap(context, states, view);
434                     }
435                 }
436                 if (faceletViewState != null)
437                 {
438                     // Make sure binding map
439                     if (oldFaceletState != null && oldFaceletState.getBindings() != null && 
440                             !oldFaceletState.getBindings().isEmpty())
441                     {
442                         // Be sure the new facelet state has the binding map filled from the old one.
443                         // When vdl.buildView() is called by restoreView, FaceletState.bindings map is filled, but
444                         // when view pool is enabled, vdl.buildView() could restore the view, but create an alternate
445                         // FaceletState instance, different from the one restored. In this case, the restored instance
446                         // has precedence, but we need to fill bindings map using the entries from the instance that
447                         // comes from the view pool.
448                         FaceletState newFaceletState = (FaceletState) faceletViewState;
449                         for (Map.Entry<String, Map<String, ValueExpression> > entry : 
450                                 oldFaceletState.getBindings().entrySet())
451                         {
452                             for (Map.Entry<String, ValueExpression> entry2 : entry.getValue().entrySet())
453                             {
454                                 ValueExpression expr = newFaceletState.getBinding(entry.getKey(), entry2.getKey());
455                                 if (expr == null)
456                                 {
457                                     newFaceletState.putBinding(entry.getKey(), entry2.getKey(), entry2.getValue());
458                                 }
459                             }
460                         }
461                         view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  newFaceletState);
462                     }
463                     else
464                     {
465                         //restore bindings
466                         view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
467                     }
468                 }
469                 if (lastUniqueIdCounter != null)
470                 {
471                     Integer newUniqueIdCounter = (Integer) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
472                     if (newUniqueIdCounter != null && 
473                         lastUniqueIdCounter.intValue() > newUniqueIdCounter.intValue())
474                     {
475                         // The unique counter was restored by a side effect of 
476                         // restoreState() over UIViewRoot with a lower count,
477                         // to avoid a component duplicate id exception we need to fix the count.
478                         view.getAttributes().put(UNIQUE_ID_COUNTER_KEY, lastUniqueIdCounter);
479                     }
480                 }
481                 handleDynamicAddedRemovedComponents(context, view, states);
482             }
483         }
484         return view;
485     }
486     
487     public void handleDynamicAddedRemovedComponents(FacesContext context, UIViewRoot view, Map<String, Object> states)
488     {
489         List<String> clientIdsRemoved = getClientIdsRemoved(view);
490 
491         if (clientIdsRemoved != null)
492         {
493             Set<String> idsRemovedSet = new HashSet<String>(HashMapUtils.calcCapacity(clientIdsRemoved.size()));
494             context.getAttributes().put(FaceletViewDeclarationLanguage.REMOVING_COMPONENTS_BUILD, Boolean.TRUE);
495             try
496             {
497                 // perf: clientIds are ArrayList: see method registerOnAddRemoveList(String)
498                 for (int i = 0, size = clientIdsRemoved.size(); i < size; i++)
499                 {
500                     String clientId = clientIdsRemoved.get(i);
501                     if (!idsRemovedSet.contains(clientId))
502                     {
503                         RemoveComponentCallback callback = new RemoveComponentCallback();
504                         view.invokeOnComponent(context, clientId, callback);
505                         if (callback.isComponentFound())
506                         {
507                             //Add only if component found
508                             idsRemovedSet.add(clientId);
509                         }
510                     }
511                 }
512                 clientIdsRemoved.clear();
513                 clientIdsRemoved.addAll(idsRemovedSet);
514             }
515             finally
516             {
517                 context.getAttributes().remove(FaceletViewDeclarationLanguage.REMOVING_COMPONENTS_BUILD);
518             }
519         }
520         List<String> clientIdsAdded = getClientIdsAdded(view);
521         if (clientIdsAdded != null)
522         {
523             Set<String> idsAddedSet = new HashSet<String>(HashMapUtils.calcCapacity(clientIdsAdded.size()));
524             // perf: clientIds are ArrayList: see method setClientsIdsAdded(String)
525             for (int i = 0, size = clientIdsAdded.size(); i < size; i++)
526             {
527                 String clientId = clientIdsAdded.get(i);
528                 if (!idsAddedSet.contains(clientId))
529                 {
530                     final AttachedFullStateWrapper wrapper = (AttachedFullStateWrapper) states.get(clientId);
531                     if (wrapper != null)
532                     {
533                         final Object[] addedState = (Object[]) wrapper.getWrappedStateObject(); 
534                         if (addedState != null)
535                         {
536                             if (addedState.length == 2)
537                             {
538                                 view = (UIViewRoot)
539                                         internalRestoreTreeStructure((TreeStructComponent) addedState[0]);
540                                 view.processRestoreState(context, addedState[1]);
541                                 break;
542                             }
543                             else
544                             {
545                                 final String parentClientId = (String) addedState[0];
546                                 view.invokeOnComponent(context, parentClientId, 
547                                     new AddComponentCallback(addedState));
548                             }
549                         }
550                     }
551                     idsAddedSet.add(clientId);
552                 }
553             }
554             // Reset this list, because it will be calculated later when the view is being saved
555             // in the right order, preventing duplicates (see COMPONENT_ADDED_AFTER_BUILD_VIEW for details).
556             clientIdsAdded.clear();
557             
558             // This call only has sense when components has been added programatically, because if facelets has control
559             // over all components in the component tree, build the initial state and apply the state will have the
560             // same effect.
561             RequestViewContext.getCurrentInstance(context).
562                     refreshRequestViewContext(context, view);
563         }
564     }
565 
566     public static class RemoveComponentCallback implements ContextCallback
567     {
568         private boolean componentFound;
569         
570         public RemoveComponentCallback()
571         {
572             this.componentFound = false;
573         }
574         
575         public void invokeContextCallback(FacesContext context,
576                 UIComponent target)
577         {
578             if (target.getParent() != null && 
579                 !target.getParent().getChildren().remove(target))
580             {
581                 String key = null;
582                 if (target.getParent().getFacetCount() > 0)
583                 {
584                     for (Map.Entry<String, UIComponent> entry :
585                             target.getParent().getFacets().entrySet())
586                     {
587                         if (entry.getValue()==target)
588                         {
589                             key = entry.getKey();
590                             break;
591                         }
592                     }
593                 }
594                 if (key != null)
595                 {
596                     UIComponent removedTarget = target.getParent().getFacets().remove(key);
597                     if (removedTarget != null)
598                     {
599                         this.componentFound = true;
600                     }
601                 }
602             }
603             else
604             {
605                 this.componentFound = true;
606             }
607         }
608         
609         public boolean isComponentFound()
610         {
611             return this.componentFound;
612         }
613     }
614 
615     public static class AddComponentCallback implements ContextCallback
616     {
617         private final Object[] addedState;
618         
619         public AddComponentCallback(Object[] addedState)
620         {
621             this.addedState = addedState;
622         }
623         
624         public void invokeContextCallback(FacesContext context,
625                 UIComponent target)
626         {
627             if (addedState[1] != null)
628             {
629                 String facetName = (String) addedState[1];
630                 UIComponent child
631                         = internalRestoreTreeStructure((TreeStructComponent)
632                                                        addedState[3]);
633                 child.processRestoreState(context, addedState[4]);
634                 target.getFacets().put(facetName,child);
635             }
636             else
637             {
638                 Integer childIndex = (Integer) addedState[2];
639                 UIComponent child
640                         = internalRestoreTreeStructure((TreeStructComponent)
641                                                        addedState[3]);
642                 child.processRestoreState(context, addedState[4]);
643                 
644                 boolean done = false;
645                 // Is the child a facelet controlled component?
646                 if (child.getAttributes().containsKey(ComponentSupport.MARK_CREATED))
647                 {
648                     // By effect of c:forEach it is possible that the component can be duplicated
649                     // in the component tree, so what we need to do as a fallback is replace the
650                     // component in the spot with the restored version.
651                     UIComponent parent = target;
652                     if (parent.getChildCount() > 0)
653                     {
654                         String tagId = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
655                         if (childIndex < parent.getChildCount())
656                         {
657                             // Try to find the component quickly 
658                             UIComponent dup = parent.getChildren().get(childIndex);
659                             if (tagId.equals(dup.getAttributes().get(ComponentSupport.MARK_CREATED)))
660                             {
661                                 // Replace
662                                 parent.getChildren().remove(childIndex.intValue());
663                                 parent.getChildren().add(childIndex, child);
664                                 done = true;
665                             }
666                         }
667                         if (!done)
668                         {
669                             // Fallback to iteration
670                             for (int i = 0, childCount = parent.getChildCount(); i < childCount; i ++)
671                             {
672                                 UIComponent dup = parent.getChildren().get(i);
673                                 if (tagId.equals(dup.getAttributes().get(ComponentSupport.MARK_CREATED)))
674                                 {
675                                     // Replace
676                                     parent.getChildren().remove(i);
677                                     parent.getChildren().add(i, child);
678                                     done = true;
679                                     break;
680                                 }
681                             }
682                         }
683                     }
684                 }
685                 if (!done)
686                 {
687                     try
688                     {
689                         target.getChildren().add(childIndex, child);
690                     }
691                     catch (IndexOutOfBoundsException e)
692                     {
693                         // We can't be sure about where should be this 
694                         // item, so just add it. 
695                         target.getChildren().add(child);
696                     }
697                 }
698             }
699         }
700     }
701 
702     @Override
703     public Object saveView (FacesContext context)
704     {
705         UIViewRoot view = context.getViewRoot();
706         Object states;
707         
708         if (view == null)
709         {
710             // Not much that can be done.
711             
712             return null;
713         }
714         
715         Object serializedView = context.getAttributes()
716             .get(SERIALIZED_VIEW_REQUEST_ATTR);
717         
718         //Note on ajax case the method saveState could be called twice: once before start
719         //document rendering and the other one when it is called StateManager.getViewState method.
720         if (serializedView == null)
721         {
722                     
723             // Make sure the client IDs are unique per the spec.
724             
725             if (context.isProjectStage(ProjectStage.Production))
726             {
727                 if (CHECK_ID_PRODUCTION_MODE_AUTO.equals(getCheckIdProductionMode(context)))
728                 {
729                     CheckDuplicateIdFaceletUtils.checkIdsStatefulComponents(context, view);
730                 }
731                 else if (CHECK_ID_PRODUCTION_MODE_TRUE.equals(getCheckIdProductionMode(context)))
732                 {
733                     CheckDuplicateIdFaceletUtils.checkIds(context, view);
734                 }
735             }
736             else
737             {
738                 CheckDuplicateIdFaceletUtils.checkIds(context, view);
739             }
740             
741             // Create save state objects for every component.
742             
743             boolean viewResetable = false;
744             int count = 0;
745             Object faceletViewState = null;
746             boolean saveViewFully = view.getAttributes().containsKey(COMPONENT_ADDED_AFTER_BUILD_VIEW);
747             if (saveViewFully)
748             {
749                 ensureClearInitialState(view);
750                 Object rlcStates = !context.getResourceLibraryContracts().isEmpty() ? 
751                     UIComponentBase.saveAttachedState(context, 
752                                 new ArrayList<String>(context.getResourceLibraryContracts())) : null;
753                 states = new Object[]{
754                             internalBuildTreeStructureToSave(view),
755                             view.processSaveState(context), rlcStates};
756             }
757             else
758             {
759                 states = new HashMap<String, Object>();
760 
761                 faceletViewState = view.getAttributes().get(ComponentSupport.FACELET_STATE_INSTANCE);
762                 if (faceletViewState != null)
763                 {
764                     ((Map<String, Object>)states).put(ComponentSupport.FACELET_STATE_INSTANCE,
765                             UIComponentBase.saveAttachedState(context, faceletViewState));
766                     //Do not save on UIViewRoot
767                     view.getAttributes().remove(ComponentSupport.FACELET_STATE_INSTANCE);
768                     view.getTransientStateHelper().putTransient(
769                             ComponentSupport.FACELET_STATE_INSTANCE, faceletViewState);
770                 }
771                 if (_viewPoolProcessor != null && 
772                     _viewPoolProcessor.isViewPoolEnabledForThisView(context, view))
773                 {
774                     SaveStateAndResetViewCallback cb = saveStateOnMapVisitTreeAndReset(
775                             context,(Map<String,Object>) states, view,
776                             Boolean.TRUE.equals(
777                         context.getAttributes().get(ViewPoolProcessor.FORCE_HARD_RESET)));
778                     viewResetable = cb.isViewResetable();
779                     count = cb.getCount();
780                 }
781                 else
782                 {
783                     saveStateOnMapVisitTree(context,(Map<String,Object>) states, view);
784                 }
785                 
786                 if ( ((Map<String,Object>)states).isEmpty())
787                 {
788                     states = null;
789                 }
790             }
791             
792             Integer uniqueIdCount = (Integer) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
793             if (uniqueIdCount != null && !uniqueIdCount.equals(1))
794             {
795                 serializedView = new Object[] { null, states, uniqueIdCount };
796             }
797             else if (states == null)
798             {
799                 serializedView = EMPTY_STATES;
800             }
801             else
802             {
803                 serializedView = new Object[] { null, states };
804             }
805             
806             //If view cache enabled store the view state into the pool
807             if (!saveViewFully && _viewPoolProcessor != null)
808             {
809                 if (viewResetable)
810                 {
811                     _viewPoolProcessor.pushResetableView(
812                         context, view, (FaceletState) faceletViewState);
813                 }
814                 else
815                 {
816                     _viewPoolProcessor.pushPartialView(
817                         context, view, (FaceletState) faceletViewState, count);
818                 }
819             }
820             
821             context.getAttributes().put(SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
822 
823         }
824         
825         return serializedView;
826     }
827     
828     private void restoreViewRootOnlyFromMap(
829             final FacesContext context, final Object viewState,
830             final UIComponent view)
831     {
832         // Only viewState found, process it but skip tree
833         // traversal, saving some time.
834         try
835         {
836             //Restore view
837             view.pushComponentToEL(context, view);
838             if (viewState != null && !(viewState instanceof AttachedFullStateWrapper))
839             {
840                 try
841                 {
842                     view.restoreState(context, viewState);
843                 }
844                 catch(Exception e)
845                 {
846                     throw new IllegalStateException(
847                             "Error restoring component: "+
848                             view.getClientId(context), e);
849                 }
850             }
851         }
852         finally
853         {
854              view.popComponentFromEL(context);
855         }
856     }
857     
858     private void restoreStateFromMap(final FacesContext context, final Map<String,Object> states,
859             final UIComponent component)
860     {
861         if (states == null)
862         {
863             return;
864         }
865         
866         try
867         {
868             //Restore view
869             component.pushComponentToEL(context, component);
870             Object state = states.get(component.getClientId(context));
871             if (state != null)
872             {
873                 if (state instanceof AttachedFullStateWrapper)
874                 {
875                     //Don't restore this one! It will be restored when the algorithm remove and add it.
876                     return;
877                 }
878                 try
879                 {
880                     component.restoreState(context, state);
881                 }
882                 catch(Exception e)
883                 {
884                     throw new IllegalStateException("Error restoring component: "+component.getClientId(context), e);
885                 }
886             }
887     
888             //Scan children
889             if (component.getChildCount() > 0)
890             {
891                 //String currentClientId = component.getClientId();
892                 
893                 List<UIComponent> children  = component.getChildren();
894                 for (int i = 0; i < children.size(); i++)
895                 {
896                     UIComponent child = children.get(i);
897                     if (child != null && !child.isTransient())
898                     {
899                         restoreStateFromMap( context, states, child);
900                     }
901                 }
902             }
903     
904             //Scan facets
905             if (component.getFacetCount() > 0)
906             {
907                 Map<String, UIComponent> facetMap = component.getFacets();
908                 
909                 for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
910                 {
911                     UIComponent child = entry.getValue();
912                     if (child != null && !child.isTransient())
913                     {
914                         //String facetName = entry.getKey();
915                         restoreStateFromMap( context, states, child);
916                     }
917                 }
918             }
919         }
920         finally
921         {
922             component.popComponentFromEL(context);
923         }
924     }
925 
926     static List<String> getClientIdsAdded(UIViewRoot root)
927     {
928         return (List<String>) root.getAttributes().get(CLIENTIDS_ADDED);
929     }
930     
931     static void setClientsIdsAdded(UIViewRoot root, List<String> clientIdsList)
932     {
933         root.getAttributes().put(CLIENTIDS_ADDED, clientIdsList);
934     }
935     
936     static List<String> getClientIdsRemoved(UIViewRoot root)
937     {
938         return (List<String>) root.getAttributes().get(CLIENTIDS_REMOVED);
939     }
940     
941     static void setClientsIdsRemoved(UIViewRoot root, List<String> clientIdsList)
942     {
943         root.getAttributes().put(CLIENTIDS_REMOVED, clientIdsList);
944     }
945     
946     @SuppressWarnings("unchecked")
947     private void registerOnAddRemoveList(FacesContext facesContext, String clientId)
948     {
949         UIViewRoot uiViewRoot = facesContext.getViewRoot();
950 
951         List<String> clientIdsAdded = (List<String>) getClientIdsAdded(uiViewRoot);
952         if (clientIdsAdded == null)
953         {
954             //Create a set that preserve insertion order
955             clientIdsAdded = new ArrayList<String>();
956         }
957         clientIdsAdded.add(clientId);
958 
959         setClientsIdsAdded(uiViewRoot, clientIdsAdded);
960 
961         List<String> clientIdsRemoved = (List<String>) getClientIdsRemoved(uiViewRoot);
962         if (clientIdsRemoved == null)
963         {
964             //Create a set that preserve insertion order
965             clientIdsRemoved = new ArrayList<String>();
966         }
967 
968         clientIdsRemoved.add(clientId);
969 
970         setClientsIdsRemoved(uiViewRoot, clientIdsRemoved);
971     }
972     
973     @SuppressWarnings("unchecked")
974     private void registerOnAddList(FacesContext facesContext, String clientId)
975     {
976         UIViewRoot uiViewRoot = facesContext.getViewRoot();
977 
978         List<String> clientIdsAdded = (List<String>) getClientIdsAdded(uiViewRoot);
979         if (clientIdsAdded == null)
980         {
981             //Create a set that preserve insertion order
982             clientIdsAdded = new ArrayList<String>();
983         }
984         clientIdsAdded.add(clientId);
985 
986         setClientsIdsAdded(uiViewRoot, clientIdsAdded);
987     }
988 
989     private void saveStateOnMapVisitTree(final FacesContext facesContext, final Map<String,Object> states,
990             final UIViewRoot uiViewRoot)
991     {
992         facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
993         try
994         {
995             uiViewRoot.visitTree( getVisitContextFactory().getVisitContext(
996                     facesContext, null, VISIT_HINTS), new VisitCallback()
997             {
998                 public VisitResult visit(VisitContext context, UIComponent target)
999                 {
1000                     FacesContext facesContext = context.getFacesContext();
1001                     Object state;
1002                     
1003                     if ((target == null) || target.isTransient())
1004                     {
1005                         // No need to bother with these components or their children.
1006                         
1007                         return VisitResult.REJECT;
1008                     }
1009                     
1010                     ComponentState componentAddedAfterBuildView
1011                             = (ComponentState) target.getAttributes().get(COMPONENT_ADDED_AFTER_BUILD_VIEW);
1012                     
1013                     //Note if UIViewRoot has this marker, JSF 1.2 like state saving is used.
1014                     if (componentAddedAfterBuildView != null && (target.getParent() != null))
1015                     {
1016                         if (ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
1017                         {
1018                             registerOnAddRemoveList(facesContext, target.getClientId(facesContext));
1019                             target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1020                         }
1021                         else if (ComponentState.ADD.equals(componentAddedAfterBuildView))
1022                         {
1023                             registerOnAddList(facesContext, target.getClientId(facesContext));
1024                             target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1025                         }
1026                         else if (ComponentState.ADDED.equals(componentAddedAfterBuildView))
1027                         {
1028                             registerOnAddList(facesContext, target.getClientId(facesContext));
1029                         }
1030                         ensureClearInitialState(target);
1031                         //Save all required info to restore the subtree.
1032                         //This includes position, structure and state of subtree
1033                         
1034                         int childIndex = target.getParent().getChildren().indexOf(target);
1035                         if (childIndex >= 0)
1036                         {
1037                             states.put(target.getClientId(facesContext), new AttachedFullStateWrapper( 
1038                                     new Object[]{
1039                                         target.getParent().getClientId(facesContext),
1040                                         null,
1041                                         childIndex,
1042                                         internalBuildTreeStructureToSave(target),
1043                                         target.processSaveState(facesContext)}));
1044                         }
1045                         else
1046                         {
1047                             String facetName = null;
1048                             if (target.getParent().getFacetCount() > 0)
1049                             {
1050                                 for (Map.Entry<String, UIComponent> entry : target.getParent().getFacets().entrySet()) 
1051                                 {
1052                                     if (target.equals(entry.getValue()))
1053                                     {
1054                                         facetName = entry.getKey();
1055                                         break;
1056                                     }
1057                                 }
1058                             }
1059                             states.put(target.getClientId(facesContext),new AttachedFullStateWrapper(new Object[]{
1060                                     target.getParent().getClientId(facesContext),
1061                                     facetName,
1062                                     null,
1063                                     internalBuildTreeStructureToSave(target),
1064                                     target.processSaveState(facesContext)}));
1065                         }
1066                         return VisitResult.REJECT;
1067                     }
1068                     else if (target.getParent() != null)
1069                     {
1070                         state = target.saveState (facesContext);
1071                         
1072                         if (state != null)
1073                         {
1074                             // Save by client ID into our map.
1075                             
1076                             states.put (target.getClientId (facesContext), state);
1077                         }
1078                         
1079                         return VisitResult.ACCEPT;
1080                     }
1081                     else
1082                     {
1083                         //Only UIViewRoot has no parent in a component tree.
1084                         return VisitResult.ACCEPT;
1085                     }
1086                 }
1087             });
1088         }
1089         finally
1090         {
1091             facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
1092         }
1093         if (!uiViewRoot.isTransient())
1094         {
1095             Object state = uiViewRoot.saveState (facesContext);
1096             if (state != null)
1097             {
1098                 // Save by client ID into our map.
1099 
1100                 states.put (uiViewRoot.getClientId (facesContext), state);
1101             }
1102         }
1103     }
1104     
1105     
1106     private SaveStateAndResetViewCallback saveStateOnMapVisitTreeAndReset(final FacesContext facesContext,
1107             final Map<String,Object> states, final UIViewRoot uiViewRoot, boolean forceHardReset)
1108     {
1109         facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
1110         SaveStateAndResetViewCallback callback = new SaveStateAndResetViewCallback(
1111                 facesContext.getViewRoot(), states, forceHardReset);
1112         if (forceHardReset)
1113         {
1114             uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
1115                     ViewPoolProcessor.RESET_MODE_HARD);
1116         }
1117         else
1118         {
1119             uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
1120                     ViewPoolProcessor.RESET_MODE_SOFT);
1121         }
1122         try
1123         {
1124             if (_viewPoolProcessor != null && 
1125                 !_viewPoolProcessor.isViewPoolEnabledForThisView(facesContext, uiViewRoot))
1126             {
1127                 callback.setViewResetable(false);
1128             }
1129             
1130             // Check if the view has removed components. If that so, it
1131             // means there is some manipulation over the component tree that
1132             // can be rollback, so it is ok to set the view as resetable.
1133             if (callback.isViewResetable())
1134             {
1135                 List<String> removedIds = getClientIdsRemoved(uiViewRoot);
1136                 if (removedIds != null && !removedIds.isEmpty())
1137                 {
1138                     callback.setViewResetable(false);
1139                 }
1140             }
1141 
1142             try
1143             {
1144                 uiViewRoot.visitTree( getVisitContextFactory().getVisitContext(
1145                         facesContext, null, VISIT_HINTS), callback);
1146             }
1147             finally
1148             {
1149                 facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
1150             }
1151             
1152             if (callback.isViewResetable() && callback.isRemoveAddedComponents())
1153             {
1154                 List<String> clientIdsToRemove = getClientIdsAdded(uiViewRoot);
1155 
1156                 if (clientIdsToRemove != null)
1157                 {
1158                     // perf: clientIds are ArrayList: see method registerOnAddRemoveList(String)
1159                     for (int i = 0, size = clientIdsToRemove.size(); i < size; i++)
1160                     {
1161                         String clientId = clientIdsToRemove.get(i);
1162                         uiViewRoot.invokeOnComponent(facesContext, clientId, new RemoveComponentCallback());
1163                     }
1164                 }
1165             }
1166 
1167             Object state = uiViewRoot.saveState (facesContext);
1168             if (state != null)
1169             {
1170                 // Save by client ID into our map.
1171                 states.put (uiViewRoot.getClientId (facesContext), state);
1172 
1173                 //Hard reset (or reset and check state again)
1174                 Integer oldResetMode = (Integer) uiViewRoot.getAttributes().put(
1175                         ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, ViewPoolProcessor.RESET_MODE_HARD);
1176                 state = uiViewRoot.saveState (facesContext);
1177                 uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, oldResetMode);
1178                 if (state != null)
1179                 {
1180                     callback.setViewResetable(false);
1181                 }
1182             }
1183         }
1184         finally
1185         {
1186             uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
1187                     ViewPoolProcessor.RESET_MODE_OFF);
1188         }
1189         return callback;
1190     }
1191     
1192     private class SaveStateAndResetViewCallback implements VisitCallback
1193     {
1194         private final Map<String, Object> states;
1195         
1196         private final UIViewRoot view;
1197         
1198         private boolean viewResetable;
1199         
1200         private boolean skipRoot;
1201         
1202         private int count;
1203         
1204         private boolean forceHardReset;
1205         
1206         private boolean removeAddedComponents;
1207         
1208         public SaveStateAndResetViewCallback(UIViewRoot view, Map<String, Object> states,
1209                 boolean forceHardReset)
1210         {
1211             this.states = states;
1212             this.view = view;
1213             this.viewResetable = true;
1214             this.skipRoot = true;
1215             this.count = 0;
1216             this.forceHardReset = forceHardReset;
1217             this.removeAddedComponents = false;
1218         }
1219         
1220         public VisitResult visit(VisitContext context, UIComponent target)
1221         {
1222             FacesContext facesContext = context.getFacesContext();
1223             Object state;
1224             this.count++;
1225 
1226             if ((target == null) || target.isTransient())
1227             {
1228                 // No need to bother with these components or their children.
1229 
1230                 return VisitResult.REJECT;
1231             }
1232             
1233             if (skipRoot && target instanceof UIViewRoot)
1234             {
1235                 //UIViewRoot should be scanned at last.
1236                 skipRoot = false;
1237                 return VisitResult.ACCEPT;
1238             }
1239 
1240             ComponentState componentAddedAfterBuildView
1241                     = (ComponentState) target.getAttributes().get(COMPONENT_ADDED_AFTER_BUILD_VIEW);
1242 
1243             //Note if UIViewRoot has this marker, JSF 1.2 like state saving is used.
1244             if (componentAddedAfterBuildView != null && (target.getParent() != null))
1245             {
1246                 //Set this view as not resetable.
1247                 //setViewResetable(false);
1248                 // Enable flag to remove added components later
1249                 setRemoveAddedComponents(true);
1250                 if (forceHardReset)
1251                 {
1252                     // The ideal is remove the added component here but visitTree does not support that
1253                     // kind of tree manipulation.
1254                     if (isViewResetable() &&
1255                         ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
1256                     {
1257                         setViewResetable(false);
1258                     }
1259                     // it is not important to save anything, skip
1260                     return VisitResult.REJECT;
1261                 }
1262                 if (ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
1263                 {
1264                     //If the view has removed components, set the view as non resetable
1265                     setViewResetable(false);
1266                     registerOnAddRemoveList(facesContext, target.getClientId(facesContext));
1267                     target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1268                 }
1269                 else if (ComponentState.ADD.equals(componentAddedAfterBuildView))
1270                 {
1271                     registerOnAddList(facesContext, target.getClientId(facesContext));
1272                     target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1273                 }
1274                 else if (ComponentState.ADDED.equals(componentAddedAfterBuildView))
1275                 {
1276                     // Later on the check of removed components we'll see if the view
1277                     // is resetable or not.
1278                     registerOnAddList(facesContext, target.getClientId(facesContext));
1279                 }
1280                 ensureClearInitialState(target);
1281                 //Save all required info to restore the subtree.
1282                 //This includes position, structure and state of subtree
1283 
1284                 int childIndex = target.getParent().getChildren().indexOf(target);
1285                 if (childIndex >= 0)
1286                 {
1287                     states.put(target.getClientId(facesContext), new AttachedFullStateWrapper( 
1288                             new Object[]{
1289                                 target.getParent().getClientId(facesContext),
1290                                 null,
1291                                 childIndex,
1292                                 internalBuildTreeStructureToSave(target),
1293                                 target.processSaveState(facesContext)}));
1294                 }
1295                 else
1296                 {
1297                     String facetName = null;
1298                     if (target.getParent().getFacetCount() > 0)
1299                     {
1300                         for (Map.Entry<String, UIComponent> entry : target.getParent().getFacets().entrySet()) 
1301                         {
1302                             if (target.equals(entry.getValue()))
1303                             {
1304                                 facetName = entry.getKey();
1305                                 break;
1306                             }
1307                         }
1308                     }
1309                     states.put(target.getClientId(facesContext),new AttachedFullStateWrapper(new Object[]{
1310                             target.getParent().getClientId(facesContext),
1311                             facetName,
1312                             null,
1313                             internalBuildTreeStructureToSave(target),
1314                             target.processSaveState(facesContext)}));
1315                 }
1316                 return VisitResult.REJECT;
1317             }
1318             else if (target.getParent() != null)
1319             {
1320                 if (forceHardReset)
1321                 {
1322                     // force hard reset set reset move on top
1323                     state = target.saveState (facesContext);
1324                     if (state != null)
1325                     {
1326                         setViewResetable(false);
1327                         return VisitResult.REJECT;
1328                     }
1329                 }
1330                 else
1331                 {
1332                     state = target.saveState (facesContext);
1333 
1334                     if (state != null)
1335                     {
1336                         // Save by client ID into our map.
1337                         states.put (target.getClientId (facesContext), state);
1338 
1339                         if (isViewResetable())
1340                         {
1341                             //Hard reset (or reset and check state again)
1342                             Integer oldResetMode = (Integer) view.getAttributes().put(
1343                                     ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
1344                                     ViewPoolProcessor.RESET_MODE_HARD);
1345                             state = target.saveState (facesContext);
1346                             view.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
1347                                     oldResetMode);
1348                             if (state != null)
1349                             {
1350                                 setViewResetable(false);
1351                             }
1352                         }
1353                     }
1354                 }
1355 
1356                 return VisitResult.ACCEPT;
1357             }
1358             else
1359             {
1360                 //Only UIViewRoot has no parent in a component tree.
1361                 return VisitResult.ACCEPT;
1362             }
1363         }
1364         
1365         /**
1366          * @return the viewResetable
1367          */
1368         public boolean isViewResetable()
1369         {
1370             return viewResetable;
1371         }
1372 
1373         /**
1374          * @param viewResetable the viewResetable to set
1375          */
1376         public void setViewResetable(boolean viewResetable)
1377         {
1378             this.viewResetable = viewResetable;
1379         }
1380         
1381         public int getCount()
1382         {
1383             return count;
1384         }
1385 
1386         /**
1387          * @return the removeAddedComponents
1388          */
1389         public boolean isRemoveAddedComponents()
1390         {
1391             return removeAddedComponents;
1392         }
1393 
1394         /**
1395          * @param removeAddedComponents the removeAddedComponents to set
1396          */
1397         public void setRemoveAddedComponents(boolean removeAddedComponents)
1398         {
1399             this.removeAddedComponents = removeAddedComponents;
1400         }
1401     }
1402     
1403     protected void ensureClearInitialState(UIComponent c)
1404     {
1405         c.clearInitialState();
1406         if (c.getChildCount() > 0)
1407         {
1408             for (int i = 0, childCount = c.getChildCount(); i < childCount; i++)
1409             {
1410                 UIComponent child = c.getChildren().get(i);
1411                 ensureClearInitialState(child);
1412             }
1413         }
1414         if (c.getFacetCount() > 0)
1415         {
1416             for (UIComponent child : c.getFacets().values())
1417             {
1418                 ensureClearInitialState(child);
1419             }
1420         }
1421     }
1422     
1423     public void suscribeListeners(UIViewRoot uiViewRoot)
1424     {
1425         boolean listenerSubscribed = false;
1426         List<SystemEventListener> pavList = uiViewRoot.getViewListenersForEventClass(PostAddToViewEvent.class);
1427         if (pavList != null)
1428         {
1429             for (SystemEventListener listener : pavList)
1430             {
1431                 if (listener instanceof PostAddPreRemoveFromViewListener)
1432                 {
1433                     listenerSubscribed = true;
1434                     break;
1435                 }
1436             }
1437         }
1438         if (!listenerSubscribed)
1439         {
1440             PostAddPreRemoveFromViewListener componentListener = new PostAddPreRemoveFromViewListener();
1441             uiViewRoot.subscribeToViewEvent(PostAddToViewEvent.class, componentListener);
1442             uiViewRoot.subscribeToViewEvent(PreRemoveFromViewEvent.class, componentListener);
1443         }
1444     }
1445     
1446     protected RenderKitFactory getRenderKitFactory()
1447     {
1448         if (_renderKitFactory == null)
1449         {
1450             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
1451         }
1452         return _renderKitFactory;
1453     }
1454     
1455     protected VisitContextFactory getVisitContextFactory()
1456     {
1457         if (_visitContextFactory == null)
1458         {
1459             _visitContextFactory = (VisitContextFactory)FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY);
1460         }
1461         return _visitContextFactory;
1462     }
1463 
1464     protected String getCheckIdProductionMode(FacesContext facesContext)
1465     {
1466         if (_checkIdsProductionMode == null)
1467         {
1468             _checkIdsProductionMode
1469                     = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
1470                     CHECK_ID_PRODUCTION_MODE, CHECK_ID_PRODUCTION_MODE_DEFAULT); //default (auto)
1471         }
1472         return _checkIdsProductionMode;
1473     }
1474 
1475     
1476     public static class PostAddPreRemoveFromViewListener implements SystemEventListener
1477     {
1478         private transient FacesContext _facesContext;
1479         
1480         private transient Boolean _isRefreshOnTransientBuildPreserveState;
1481 
1482         public boolean isListenerForSource(Object source)
1483         {
1484             // PostAddToViewEvent and PreRemoveFromViewEvent are
1485             // called from UIComponentBase.setParent
1486             return (source instanceof UIComponent);
1487         }
1488         
1489         private boolean isRefreshOnTransientBuildPreserveState()
1490         {
1491             if (_isRefreshOnTransientBuildPreserveState == null)
1492             {
1493                 _isRefreshOnTransientBuildPreserveState = MyfacesConfig.getCurrentInstance(
1494                         _facesContext.getExternalContext()).isRefreshTransientBuildOnPSSPreserveState();
1495             }
1496             return _isRefreshOnTransientBuildPreserveState;
1497         }
1498 
1499         public void processEvent(SystemEvent event)
1500         {
1501             UIComponent component = (UIComponent) event.getSource();
1502             
1503             if (component.isTransient())
1504             {
1505                 return;
1506             }
1507             
1508             // This is a view listener. It is not saved on the state and this listener
1509             // is suscribed each time the view is restored, so we can cache facesContext
1510             // here
1511             if (_facesContext == null)
1512             {
1513                 _facesContext = FacesContext.getCurrentInstance();
1514             }
1515             
1516             if (event instanceof PostAddToViewEvent)
1517             {
1518                 if (!isRefreshOnTransientBuildPreserveState() &&
1519                     Boolean.TRUE.equals(_facesContext.getAttributes().get(StateManager.IS_BUILDING_INITIAL_STATE)))
1520                 {
1521                     return;
1522                 }
1523 
1524                 //PostAddToViewEvent
1525                 component.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADD);
1526             }
1527             else
1528             {
1529                 //FacesContext facesContext = FacesContext.getCurrentInstance();
1530                 // In this case if we are removing components on build, it is not necessary to register
1531                 // again the current id, and its more, it could cause a concurrent exception. But note
1532                 // we need to propagate PreRemoveFromViewEvent, otherwise the view will not be restored
1533                 // correctly.
1534                 if (FaceletViewDeclarationLanguage.isRemovingComponentBuild(_facesContext))
1535                 {
1536                     return;
1537                 }
1538 
1539                 if (!isRefreshOnTransientBuildPreserveState() &&
1540                     FaceletCompositionContext.getCurrentInstance(_facesContext) != null &&
1541                     (component.getAttributes().containsKey(ComponentSupport.MARK_CREATED) ||
1542                      component.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
1543                     )
1544                 {
1545                     // Components removed by facelets algorithm does not need to be registered
1546                     // unless preserve state mode is used, because PSS initial state is changed
1547                     // to restore delta properly.
1548                     // MYFACES-3554 It is possible to find use cases where a component
1549                     // created by a facelet tag is changed dynamically in some way in render
1550                     // response time, so we need to check here also when facelets algorithm
1551                     // is running or not. 
1552                     return;
1553                 }
1554                 
1555                 //PreRemoveFromViewEvent
1556                 UIViewRoot uiViewRoot = _facesContext.getViewRoot();
1557                 
1558                 List<String> clientIdsRemoved = getClientIdsRemoved(uiViewRoot);
1559                 if (clientIdsRemoved == null)
1560                 {
1561                     //Create a set that preserve insertion order
1562                     clientIdsRemoved = new ArrayList<String>();
1563                 }
1564                 clientIdsRemoved.add(component.getClientId(_facesContext));
1565                 setClientsIdsRemoved(uiViewRoot, clientIdsRemoved);
1566             }
1567         }
1568     }
1569     
1570     private static TreeStructComponent internalBuildTreeStructureToSave(UIComponent component)
1571     {
1572         TreeStructComponent structComp = new TreeStructComponent(component.getClass().getName(),
1573                                                                  component.getId());
1574 
1575         //children
1576         if (component.getChildCount() > 0)
1577         {
1578             List<TreeStructComponent> structChildList = new ArrayList<TreeStructComponent>();
1579             for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
1580             {
1581                 UIComponent child = component.getChildren().get(i);     
1582                 if (!child.isTransient())
1583                 {
1584                     TreeStructComponent structChild = internalBuildTreeStructureToSave(child);
1585                     structChildList.add(structChild);
1586                 }
1587             }
1588             
1589             TreeStructComponent[] childArray = structChildList.toArray(new TreeStructComponent[structChildList.size()]);
1590             structComp.setChildren(childArray);
1591         }
1592 
1593         //facets
1594         
1595         if (component.getFacetCount() > 0)
1596         {
1597             Map<String, UIComponent> facetMap = component.getFacets();
1598             List<Object[]> structFacetList = new ArrayList<Object[]>();
1599             for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
1600             {
1601                 UIComponent child = entry.getValue();
1602                 if (!child.isTransient())
1603                 {
1604                     String facetName = entry.getKey();
1605                     TreeStructComponent structChild = internalBuildTreeStructureToSave(child);
1606                     structFacetList.add(new Object[] {facetName, structChild});
1607                 }
1608             }
1609             
1610             Object[] facetArray = structFacetList.toArray(new Object[structFacetList.size()]);
1611             structComp.setFacets(facetArray);
1612         }
1613 
1614         return structComp;
1615     }
1616     
1617     private static UIComponent internalRestoreTreeStructure(TreeStructComponent treeStructComp)
1618     {
1619         String compClass = treeStructComp.getComponentClass();
1620         String compId = treeStructComp.getComponentId();
1621         UIComponent component = (UIComponent)ClassUtils.newInstance(compClass);
1622         component.setId(compId);
1623 
1624         //children
1625         TreeStructComponent[] childArray = treeStructComp.getChildren();
1626         if (childArray != null)
1627         {
1628             List<UIComponent> childList = component.getChildren();
1629             for (int i = 0, len = childArray.length; i < len; i++)
1630             {
1631                 UIComponent child = internalRestoreTreeStructure(childArray[i]);
1632                 childList.add(child);
1633             }
1634         }
1635 
1636         //facets
1637         Object[] facetArray = treeStructComp.getFacets();
1638         if (facetArray != null)
1639         {
1640             Map<String, UIComponent> facetMap = component.getFacets();
1641             for (int i = 0, len = facetArray.length; i < len; i++)
1642             {
1643                 Object[] tuple = (Object[])facetArray[i];
1644                 String facetName = (String)tuple[0];
1645                 TreeStructComponent structChild = (TreeStructComponent)tuple[1];
1646                 UIComponent child = internalRestoreTreeStructure(structChild);
1647                 facetMap.put(facetName, child);
1648             }
1649         }
1650 
1651         return component;
1652     }
1653 
1654     public static class TreeStructComponent implements Serializable
1655     {
1656         private static final long serialVersionUID = 5069109074684737231L;
1657         private String _componentClass;
1658         private String _componentId;
1659         private TreeStructComponent[] _children = null; // Array of children
1660         private Object[] _facets = null; // Array of Array-tuples with Facetname and TreeStructComponent
1661 
1662         TreeStructComponent(String componentClass, String componentId)
1663         {
1664             _componentClass = componentClass;
1665             _componentId = componentId;
1666         }
1667 
1668         public String getComponentClass()
1669         {
1670             return _componentClass;
1671         }
1672 
1673         public String getComponentId()
1674         {
1675             return _componentId;
1676         }
1677 
1678         void setChildren(TreeStructComponent[] children)
1679         {
1680             _children = children;
1681         }
1682 
1683         TreeStructComponent[] getChildren()
1684         {
1685             return _children;
1686         }
1687 
1688         Object[] getFacets()
1689         {
1690             return _facets;
1691         }
1692 
1693         void setFacets(Object[] facets)
1694         {
1695             _facets = facets;
1696         }
1697     }
1698     
1699 }