Coverage Report - org.apache.myfaces.view.facelets.DefaultFaceletsStateManagementStrategy
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultFaceletsStateManagementStrategy
0%
0/375
0%
0/250
5.778
DefaultFaceletsStateManagementStrategy$1
0%
0/33
0%
0/26
5.778
DefaultFaceletsStateManagementStrategy$AddComponentCallback
0%
0/37
0%
0/18
5.778
DefaultFaceletsStateManagementStrategy$PostAddPreRemoveFromViewListener
0%
0/25
0%
0/24
5.778
DefaultFaceletsStateManagementStrategy$RemoveComponentCallback
0%
0/19
0%
0/14
5.778
DefaultFaceletsStateManagementStrategy$SaveStateAndResetViewCallback
0%
0/69
0%
0/44
5.778
DefaultFaceletsStateManagementStrategy$TreeStructComponent
0%
0/14
N/A
5.778
 
 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  0
 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  0
     private static final String SERIALIZED_VIEW_REQUEST_ATTR = 
 169  
         StateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
 170  
     
 171  0
     private static final Object[] EMPTY_STATES = new Object[]{null, null};
 172  
     
 173  0
     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  0
     private RenderKitFactory _renderKitFactory = null;
 182  
     
 183  0
     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  0
         this(FacesContext.getCurrentInstance());
 194  0
     }
 195  
     
 196  
     public DefaultFaceletsStateManagementStrategy (FacesContext context)
 197  0
     {
 198  0
         _vdlFactory = (ViewDeclarationLanguageFactory)
 199  
                 FactoryFinder.getFactory(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY);
 200  0
         _config = MyfacesConfig.getCurrentInstance(context.getExternalContext());
 201  0
         _viewPoolProcessor = ViewPoolProcessor.getInstance(context);
 202  0
     }
 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  0
         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  0
         final boolean oldContextEventState = context.isProcessingEvents();
 216  
         // Get previous state from ResponseStateManager.
 217  0
         manager = getRenderKitFactory().getRenderKit(context, renderKitId).getResponseStateManager();
 218  
         
 219  0
         state = (Object[]) manager.getState(context, viewId);
 220  
         
 221  0
         if (state == null)
 222  
         {
 223  
             //No state could be restored, return null causing ViewExpiredException
 224  0
             return null;
 225  
         }
 226  
         
 227  0
         if (state[1] instanceof Object[])
 228  
         {
 229  0
             Object[] fullState = (Object[]) state[1]; 
 230  0
             view = (UIViewRoot) internalRestoreTreeStructure((TreeStructComponent)fullState[0]);
 231  
 
 232  0
             if (view != null)
 233  
             {
 234  0
                 context.setViewRoot (view);
 235  0
                 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  0
                 RequestViewContext.getCurrentInstance(context).
 241  
                         refreshRequestViewContext(context, view);
 242  
                 
 243  0
                 if (fullState.length == 3 && fullState[2] != null)
 244  
                 {
 245  0
                     context.setResourceLibraryContracts((List) UIComponentBase.
 246  
                         restoreAttachedState(context, fullState[2]));
 247  
                 }
 248  
             }
 249  0
         }
 250  
         else
 251  
         {
 252  
             // Per the spec: build the view.
 253  0
             ViewDeclarationLanguage vdl = _vdlFactory.getViewDeclarationLanguage(viewId);
 254  0
             Object faceletViewState = null;
 255  
             try
 256  
             {
 257  0
                 ViewMetadata metadata = vdl.getViewMetadata (context, viewId);
 258  
                 
 259  0
                 if (metadata != null)
 260  
                 {
 261  0
                     view = metadata.createMetadataView(context);
 262  
                     
 263  
                     // If no view and response complete there is no need to continue
 264  0
                     if (view == null && context.getResponseComplete())
 265  
                     {
 266  0
                         return null;
 267  
                     }
 268  
                 }
 269  0
                 if (view == null)
 270  
                 {
 271  0
                     view = context.getApplication().getViewHandler().createView(context, viewId);
 272  
                 }
 273  
                 
 274  0
                 context.setViewRoot (view); 
 275  0
                 boolean skipBuildView = false;
 276  0
                 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  0
                     states = (Map<String, Object>) state[1];
 284  0
                     faceletViewState = UIComponentBase.restoreAttachedState(
 285  
                             context,states.get(ComponentSupport.FACELET_STATE_INSTANCE));
 286  0
                     if (faceletViewState != null && _viewPoolProcessor != null)
 287  
                     {
 288  0
                         ViewPool viewPool = _viewPoolProcessor.getViewPool(context, view);
 289  0
                         if (viewPool != null)
 290  
                         {
 291  0
                             ViewStructureMetadata viewMetadata = viewPool.retrieveDynamicViewStructureMetadata(
 292  
                                 context, view, (FaceletState) faceletViewState);
 293  0
                             if (viewMetadata != null)
 294  
                             {
 295  0
                                 ViewEntry entry = viewPool.popDynamicStructureView(context, view,
 296  
                                         (FaceletState) faceletViewState);
 297  0
                                 if (entry != null)
 298  
                                 {
 299  0
                                     skipBuildView = true;
 300  0
                                     _viewPoolProcessor.cloneAndRestoreView(context, view, entry, viewMetadata);
 301  
                                 }
 302  
                             }
 303  
                         }
 304  
                     }
 305  0
                     if (view.getId() == null)
 306  
                     {
 307  0
                         view.setId(view.createUniqueId(context, null));
 308  
                     }
 309  0
                     if (faceletViewState != null)
 310  
                     {
 311  
                         //if (skipBuildView)
 312  
                         //{
 313  0
                             FaceletState newFaceletState = (FaceletState) view.getAttributes().get(
 314  
                                     ComponentSupport.FACELET_STATE_INSTANCE);
 315  0
                             if (newFaceletState != null)
 316  
                             {
 317  0
                                 newFaceletState.restoreState(context, 
 318  
                                         ((FaceletState)faceletViewState).saveState(context));
 319  0
                                 faceletViewState = newFaceletState;
 320  
                             }
 321  
                             else
 322  
                             {
 323  0
                                 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  0
                     if (state.length == 3)
 332  
                     {
 333  
                         //Jump to where the count is
 334  0
                         view.getAttributes().put(UNIQUE_ID_COUNTER_KEY, state[2]);
 335  
                     }
 336  0
                     Object viewRootState = states.get(view.getClientId(context));
 337  0
                     if (viewRootState != null)
 338  
                     {
 339  
                         try
 340  
                         {
 341  0
                             view.pushComponentToEL(context, view);
 342  0
                             view.restoreViewScopeState(context, viewRootState);
 343  
                         }
 344  
                         finally
 345  
                         {
 346  0
                             view.popComponentFromEL(context);
 347  0
                         }
 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  0
                 if (!skipBuildView)
 355  
                 {
 356  
                     try 
 357  
                     {
 358  0
                         context.setProcessingEvents (true);
 359  0
                         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  0
                         suscribeListeners(view);
 364  
                     }
 365  
                     finally
 366  
                     {
 367  0
                         context.setProcessingEvents (oldContextEventState);
 368  0
                     }
 369  
                 }
 370  
             }
 371  0
             catch (Throwable e)
 372  
             {
 373  0
                 throw new FacesException ("unable to create view \"" + viewId + "\"", e);
 374  0
             }
 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  0
             boolean statelessMode = manager.isStateless(context, viewId);
 379  0
             if (statelessMode && !view.isTransient())
 380  
             {
 381  0
                 throw new IllegalStateException("View is not transient");
 382  
             }
 383  0
             if (!statelessMode && view.isTransient())
 384  
             {
 385  0
                 throw new IllegalStateException("Cannot apply state over stateless view");
 386  
             }
 387  
             
 388  0
             if (state != null && state[1] != null)
 389  
             {
 390  0
                 states = (Map<String, Object>) state[1];
 391  
                 //Save the last unique id counter key in UIViewRoot
 392  0
                 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  0
                 FaceletState oldFaceletState = (FaceletState) view.getAttributes().get(
 396  
                         ComponentSupport.FACELET_STATE_INSTANCE);
 397  
                 
 398  
                 // Visit the children and restore their state.
 399  0
                 boolean emptyState = false;
 400  0
                 boolean containsFaceletState = states.containsKey(
 401  
                         ComponentSupport.FACELET_STATE_INSTANCE);
 402  0
                 if (states.isEmpty())
 403  
                 {
 404  0
                     emptyState = true; 
 405  
                 }
 406  0
                 else if (states.size() == 1 && 
 407  
                         containsFaceletState)
 408  
                 {
 409  0
                     emptyState = true; 
 410  
                 }
 411  
                 //Restore state of current components
 412  0
                 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  0
                     if ((states.size() == 1 && !containsFaceletState) || 
 418  
                         (states.size() == 2 && containsFaceletState))
 419  
                     {
 420  0
                         Object viewState = states.get(view.getClientId(context));
 421  0
                         if (viewState != null)
 422  
                         {
 423  0
                             restoreViewRootOnlyFromMap(context,viewState, view);
 424  
                         }
 425  
                         else
 426  
                         {
 427  
                             //The component is not viewRoot, restore as usual.
 428  0
                             restoreStateFromMap(context, states, view);
 429  
                         }
 430  0
                     }
 431  
                     else
 432  
                     {
 433  0
                         restoreStateFromMap(context, states, view);
 434  
                     }
 435  
                 }
 436  0
                 if (faceletViewState != null)
 437  
                 {
 438  
                     // Make sure binding map
 439  0
                     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  0
                         FaceletState newFaceletState = (FaceletState) faceletViewState;
 449  
                         for (Map.Entry<String, Map<String, ValueExpression> > entry : 
 450  0
                                 oldFaceletState.getBindings().entrySet())
 451  
                         {
 452  0
                             for (Map.Entry<String, ValueExpression> entry2 : entry.getValue().entrySet())
 453  
                             {
 454  0
                                 ValueExpression expr = newFaceletState.getBinding(entry.getKey(), entry2.getKey());
 455  0
                                 if (expr == null)
 456  
                                 {
 457  0
                                     newFaceletState.putBinding(entry.getKey(), entry2.getKey(), entry2.getValue());
 458  
                                 }
 459  0
                             }
 460  0
                         }
 461  0
                         view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  newFaceletState);
 462  0
                     }
 463  
                     else
 464  
                     {
 465  
                         //restore bindings
 466  0
                         view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
 467  
                     }
 468  
                 }
 469  0
                 if (lastUniqueIdCounter != null)
 470  
                 {
 471  0
                     Integer newUniqueIdCounter = (Integer) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
 472  0
                     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  0
                         view.getAttributes().put(UNIQUE_ID_COUNTER_KEY, lastUniqueIdCounter);
 479  
                     }
 480  
                 }
 481  0
                 handleDynamicAddedRemovedComponents(context, view, states);
 482  
             }
 483  
         }
 484  0
         return view;
 485  
     }
 486  
     
 487  
     public void handleDynamicAddedRemovedComponents(FacesContext context, UIViewRoot view, Map<String, Object> states)
 488  
     {
 489  0
         List<String> clientIdsRemoved = getClientIdsRemoved(view);
 490  
 
 491  0
         if (clientIdsRemoved != null)
 492  
         {
 493  0
             Set<String> idsRemovedSet = new HashSet<String>(HashMapUtils.calcCapacity(clientIdsRemoved.size()));
 494  0
             context.getAttributes().put(FaceletViewDeclarationLanguage.REMOVING_COMPONENTS_BUILD, Boolean.TRUE);
 495  
             try
 496  
             {
 497  
                 // perf: clientIds are ArrayList: see method registerOnAddRemoveList(String)
 498  0
                 for (int i = 0, size = clientIdsRemoved.size(); i < size; i++)
 499  
                 {
 500  0
                     String clientId = clientIdsRemoved.get(i);
 501  0
                     if (!idsRemovedSet.contains(clientId))
 502  
                     {
 503  0
                         RemoveComponentCallback callback = new RemoveComponentCallback();
 504  0
                         view.invokeOnComponent(context, clientId, callback);
 505  0
                         if (callback.isComponentFound())
 506  
                         {
 507  
                             //Add only if component found
 508  0
                             idsRemovedSet.add(clientId);
 509  
                         }
 510  
                     }
 511  
                 }
 512  0
                 clientIdsRemoved.clear();
 513  0
                 clientIdsRemoved.addAll(idsRemovedSet);
 514  
             }
 515  
             finally
 516  
             {
 517  0
                 context.getAttributes().remove(FaceletViewDeclarationLanguage.REMOVING_COMPONENTS_BUILD);
 518  0
             }
 519  
         }
 520  0
         List<String> clientIdsAdded = getClientIdsAdded(view);
 521  0
         if (clientIdsAdded != null)
 522  
         {
 523  0
             Set<String> idsAddedSet = new HashSet<String>(HashMapUtils.calcCapacity(clientIdsAdded.size()));
 524  
             // perf: clientIds are ArrayList: see method setClientsIdsAdded(String)
 525  0
             for (int i = 0, size = clientIdsAdded.size(); i < size; i++)
 526  
             {
 527  0
                 String clientId = clientIdsAdded.get(i);
 528  0
                 if (!idsAddedSet.contains(clientId))
 529  
                 {
 530  0
                     final AttachedFullStateWrapper wrapper = (AttachedFullStateWrapper) states.get(clientId);
 531  0
                     if (wrapper != null)
 532  
                     {
 533  0
                         final Object[] addedState = (Object[]) wrapper.getWrappedStateObject(); 
 534  0
                         if (addedState != null)
 535  
                         {
 536  0
                             if (addedState.length == 2)
 537  
                             {
 538  0
                                 view = (UIViewRoot)
 539  
                                         internalRestoreTreeStructure((TreeStructComponent) addedState[0]);
 540  0
                                 view.processRestoreState(context, addedState[1]);
 541  0
                                 break;
 542  
                             }
 543  
                             else
 544  
                             {
 545  0
                                 final String parentClientId = (String) addedState[0];
 546  0
                                 view.invokeOnComponent(context, parentClientId, 
 547  
                                     new AddComponentCallback(addedState));
 548  
                             }
 549  
                         }
 550  
                     }
 551  0
                     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  0
             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  0
             RequestViewContext.getCurrentInstance(context).
 562  
                     refreshRequestViewContext(context, view);
 563  
         }
 564  0
     }
 565  
 
 566  
     public static class RemoveComponentCallback implements ContextCallback
 567  
     {
 568  
         private boolean componentFound;
 569  
         
 570  
         public RemoveComponentCallback()
 571  0
         {
 572  0
             this.componentFound = false;
 573  0
         }
 574  
         
 575  
         public void invokeContextCallback(FacesContext context,
 576  
                 UIComponent target)
 577  
         {
 578  0
             if (target.getParent() != null && 
 579  
                 !target.getParent().getChildren().remove(target))
 580  
             {
 581  0
                 String key = null;
 582  0
                 if (target.getParent().getFacetCount() > 0)
 583  
                 {
 584  
                     for (Map.Entry<String, UIComponent> entry :
 585  0
                             target.getParent().getFacets().entrySet())
 586  
                     {
 587  0
                         if (entry.getValue()==target)
 588  
                         {
 589  0
                             key = entry.getKey();
 590  0
                             break;
 591  
                         }
 592  0
                     }
 593  
                 }
 594  0
                 if (key != null)
 595  
                 {
 596  0
                     UIComponent removedTarget = target.getParent().getFacets().remove(key);
 597  0
                     if (removedTarget != null)
 598  
                     {
 599  0
                         this.componentFound = true;
 600  
                     }
 601  
                 }
 602  0
             }
 603  
             else
 604  
             {
 605  0
                 this.componentFound = true;
 606  
             }
 607  0
         }
 608  
         
 609  
         public boolean isComponentFound()
 610  
         {
 611  0
             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  0
         {
 621  0
             this.addedState = addedState;
 622  0
         }
 623  
         
 624  
         public void invokeContextCallback(FacesContext context,
 625  
                 UIComponent target)
 626  
         {
 627  0
             if (addedState[1] != null)
 628  
             {
 629  0
                 String facetName = (String) addedState[1];
 630  0
                 UIComponent child
 631  
                         = internalRestoreTreeStructure((TreeStructComponent)
 632  
                                                        addedState[3]);
 633  0
                 child.processRestoreState(context, addedState[4]);
 634  0
                 target.getFacets().put(facetName,child);
 635  0
             }
 636  
             else
 637  
             {
 638  0
                 Integer childIndex = (Integer) addedState[2];
 639  0
                 UIComponent child
 640  
                         = internalRestoreTreeStructure((TreeStructComponent)
 641  
                                                        addedState[3]);
 642  0
                 child.processRestoreState(context, addedState[4]);
 643  
                 
 644  0
                 boolean done = false;
 645  
                 // Is the child a facelet controlled component?
 646  0
                 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  0
                     UIComponent parent = target;
 652  0
                     if (parent.getChildCount() > 0)
 653  
                     {
 654  0
                         String tagId = (String) child.getAttributes().get(ComponentSupport.MARK_CREATED);
 655  0
                         if (childIndex < parent.getChildCount())
 656  
                         {
 657  
                             // Try to find the component quickly 
 658  0
                             UIComponent dup = parent.getChildren().get(childIndex);
 659  0
                             if (tagId.equals(dup.getAttributes().get(ComponentSupport.MARK_CREATED)))
 660  
                             {
 661  
                                 // Replace
 662  0
                                 parent.getChildren().remove(childIndex.intValue());
 663  0
                                 parent.getChildren().add(childIndex, child);
 664  0
                                 done = true;
 665  
                             }
 666  
                         }
 667  0
                         if (!done)
 668  
                         {
 669  
                             // Fallback to iteration
 670  0
                             for (int i = 0, childCount = parent.getChildCount(); i < childCount; i ++)
 671  
                             {
 672  0
                                 UIComponent dup = parent.getChildren().get(i);
 673  0
                                 if (tagId.equals(dup.getAttributes().get(ComponentSupport.MARK_CREATED)))
 674  
                                 {
 675  
                                     // Replace
 676  0
                                     parent.getChildren().remove(i);
 677  0
                                     parent.getChildren().add(i, child);
 678  0
                                     done = true;
 679  0
                                     break;
 680  
                                 }
 681  
                             }
 682  
                         }
 683  
                     }
 684  
                 }
 685  0
                 if (!done)
 686  
                 {
 687  
                     try
 688  
                     {
 689  0
                         target.getChildren().add(childIndex, child);
 690  
                     }
 691  0
                     catch (IndexOutOfBoundsException e)
 692  
                     {
 693  
                         // We can't be sure about where should be this 
 694  
                         // item, so just add it. 
 695  0
                         target.getChildren().add(child);
 696  0
                     }
 697  
                 }
 698  
             }
 699  0
         }
 700  
     }
 701  
 
 702  
     @Override
 703  
     public Object saveView (FacesContext context)
 704  
     {
 705  0
         UIViewRoot view = context.getViewRoot();
 706  
         Object states;
 707  
         
 708  0
         if (view == null)
 709  
         {
 710  
             // Not much that can be done.
 711  
             
 712  0
             return null;
 713  
         }
 714  
         
 715  0
         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  0
         if (serializedView == null)
 721  
         {
 722  
                     
 723  
             // Make sure the client IDs are unique per the spec.
 724  
             
 725  0
             if (context.isProjectStage(ProjectStage.Production))
 726  
             {
 727  0
                 if (CHECK_ID_PRODUCTION_MODE_AUTO.equals(getCheckIdProductionMode(context)))
 728  
                 {
 729  0
                     CheckDuplicateIdFaceletUtils.checkIdsStatefulComponents(context, view);
 730  
                 }
 731  0
                 else if (CHECK_ID_PRODUCTION_MODE_TRUE.equals(getCheckIdProductionMode(context)))
 732  
                 {
 733  0
                     CheckDuplicateIdFaceletUtils.checkIds(context, view);
 734  
                 }
 735  
             }
 736  
             else
 737  
             {
 738  0
                 CheckDuplicateIdFaceletUtils.checkIds(context, view);
 739  
             }
 740  
             
 741  
             // Create save state objects for every component.
 742  
             
 743  0
             boolean viewResetable = false;
 744  0
             int count = 0;
 745  0
             Object faceletViewState = null;
 746  0
             boolean saveViewFully = view.getAttributes().containsKey(COMPONENT_ADDED_AFTER_BUILD_VIEW);
 747  0
             if (saveViewFully)
 748  
             {
 749  0
                 ensureClearInitialState(view);
 750  0
                 Object rlcStates = !context.getResourceLibraryContracts().isEmpty() ? 
 751  
                     UIComponentBase.saveAttachedState(context, 
 752  
                                 new ArrayList<String>(context.getResourceLibraryContracts())) : null;
 753  0
                 states = new Object[]{
 754  
                             internalBuildTreeStructureToSave(view),
 755  
                             view.processSaveState(context), rlcStates};
 756  0
             }
 757  
             else
 758  
             {
 759  0
                 states = new HashMap<String, Object>();
 760  
 
 761  0
                 faceletViewState = view.getAttributes().get(ComponentSupport.FACELET_STATE_INSTANCE);
 762  0
                 if (faceletViewState != null)
 763  
                 {
 764  0
                     ((Map<String, Object>)states).put(ComponentSupport.FACELET_STATE_INSTANCE,
 765  
                             UIComponentBase.saveAttachedState(context, faceletViewState));
 766  
                     //Do not save on UIViewRoot
 767  0
                     view.getAttributes().remove(ComponentSupport.FACELET_STATE_INSTANCE);
 768  0
                     view.getTransientStateHelper().putTransient(
 769  
                             ComponentSupport.FACELET_STATE_INSTANCE, faceletViewState);
 770  
                 }
 771  0
                 if (_viewPoolProcessor != null && 
 772  
                     _viewPoolProcessor.isViewPoolEnabledForThisView(context, view))
 773  
                 {
 774  0
                     SaveStateAndResetViewCallback cb = saveStateOnMapVisitTreeAndReset(
 775  
                             context,(Map<String,Object>) states, view,
 776  
                             Boolean.TRUE.equals(
 777  
                         context.getAttributes().get(ViewPoolProcessor.FORCE_HARD_RESET)));
 778  0
                     viewResetable = cb.isViewResetable();
 779  0
                     count = cb.getCount();
 780  0
                 }
 781  
                 else
 782  
                 {
 783  0
                     saveStateOnMapVisitTree(context,(Map<String,Object>) states, view);
 784  
                 }
 785  
                 
 786  0
                 if ( ((Map<String,Object>)states).isEmpty())
 787  
                 {
 788  0
                     states = null;
 789  
                 }
 790  
             }
 791  
             
 792  0
             Integer uniqueIdCount = (Integer) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
 793  0
             if (uniqueIdCount != null && !uniqueIdCount.equals(1))
 794  
             {
 795  0
                 serializedView = new Object[] { null, states, uniqueIdCount };
 796  
             }
 797  0
             else if (states == null)
 798  
             {
 799  0
                 serializedView = EMPTY_STATES;
 800  
             }
 801  
             else
 802  
             {
 803  0
                 serializedView = new Object[] { null, states };
 804  
             }
 805  
             
 806  
             //If view cache enabled store the view state into the pool
 807  0
             if (!saveViewFully && _viewPoolProcessor != null)
 808  
             {
 809  0
                 if (viewResetable)
 810  
                 {
 811  0
                     _viewPoolProcessor.pushResetableView(
 812  
                         context, view, (FaceletState) faceletViewState);
 813  
                 }
 814  
                 else
 815  
                 {
 816  0
                     _viewPoolProcessor.pushPartialView(
 817  
                         context, view, (FaceletState) faceletViewState, count);
 818  
                 }
 819  
             }
 820  
             
 821  0
             context.getAttributes().put(SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
 822  
 
 823  
         }
 824  
         
 825  0
         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  0
             view.pushComponentToEL(context, view);
 838  0
             if (viewState != null && !(viewState instanceof AttachedFullStateWrapper))
 839  
             {
 840  
                 try
 841  
                 {
 842  0
                     view.restoreState(context, viewState);
 843  
                 }
 844  0
                 catch(Exception e)
 845  
                 {
 846  0
                     throw new IllegalStateException(
 847  
                             "Error restoring component: "+
 848  
                             view.getClientId(context), e);
 849  0
                 }
 850  
             }
 851  
         }
 852  
         finally
 853  
         {
 854  0
              view.popComponentFromEL(context);
 855  0
         }
 856  0
     }
 857  
     
 858  
     private void restoreStateFromMap(final FacesContext context, final Map<String,Object> states,
 859  
             final UIComponent component)
 860  
     {
 861  0
         if (states == null)
 862  
         {
 863  0
             return;
 864  
         }
 865  
         
 866  
         try
 867  
         {
 868  
             //Restore view
 869  0
             component.pushComponentToEL(context, component);
 870  0
             Object state = states.get(component.getClientId(context));
 871  0
             if (state != null)
 872  
             {
 873  0
                 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  0
                     component.restoreState(context, state);
 881  
                 }
 882  0
                 catch(Exception e)
 883  
                 {
 884  0
                     throw new IllegalStateException("Error restoring component: "+component.getClientId(context), e);
 885  0
                 }
 886  
             }
 887  
     
 888  
             //Scan children
 889  0
             if (component.getChildCount() > 0)
 890  
             {
 891  
                 //String currentClientId = component.getClientId();
 892  
                 
 893  0
                 List<UIComponent> children  = component.getChildren();
 894  0
                 for (int i = 0; i < children.size(); i++)
 895  
                 {
 896  0
                     UIComponent child = children.get(i);
 897  0
                     if (child != null && !child.isTransient())
 898  
                     {
 899  0
                         restoreStateFromMap( context, states, child);
 900  
                     }
 901  
                 }
 902  
             }
 903  
     
 904  
             //Scan facets
 905  0
             if (component.getFacetCount() > 0)
 906  
             {
 907  0
                 Map<String, UIComponent> facetMap = component.getFacets();
 908  
                 
 909  0
                 for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
 910  
                 {
 911  0
                     UIComponent child = entry.getValue();
 912  0
                     if (child != null && !child.isTransient())
 913  
                     {
 914  
                         //String facetName = entry.getKey();
 915  0
                         restoreStateFromMap( context, states, child);
 916  
                     }
 917  0
                 }
 918  
             }
 919  
         }
 920  
         finally
 921  
         {
 922  0
             component.popComponentFromEL(context);
 923  0
         }
 924  0
     }
 925  
 
 926  
     static List<String> getClientIdsAdded(UIViewRoot root)
 927  
     {
 928  0
         return (List<String>) root.getAttributes().get(CLIENTIDS_ADDED);
 929  
     }
 930  
     
 931  
     static void setClientsIdsAdded(UIViewRoot root, List<String> clientIdsList)
 932  
     {
 933  0
         root.getAttributes().put(CLIENTIDS_ADDED, clientIdsList);
 934  0
     }
 935  
     
 936  
     static List<String> getClientIdsRemoved(UIViewRoot root)
 937  
     {
 938  0
         return (List<String>) root.getAttributes().get(CLIENTIDS_REMOVED);
 939  
     }
 940  
     
 941  
     static void setClientsIdsRemoved(UIViewRoot root, List<String> clientIdsList)
 942  
     {
 943  0
         root.getAttributes().put(CLIENTIDS_REMOVED, clientIdsList);
 944  0
     }
 945  
     
 946  
     @SuppressWarnings("unchecked")
 947  
     private void registerOnAddRemoveList(FacesContext facesContext, String clientId)
 948  
     {
 949  0
         UIViewRoot uiViewRoot = facesContext.getViewRoot();
 950  
 
 951  0
         List<String> clientIdsAdded = (List<String>) getClientIdsAdded(uiViewRoot);
 952  0
         if (clientIdsAdded == null)
 953  
         {
 954  
             //Create a set that preserve insertion order
 955  0
             clientIdsAdded = new ArrayList<String>();
 956  
         }
 957  0
         clientIdsAdded.add(clientId);
 958  
 
 959  0
         setClientsIdsAdded(uiViewRoot, clientIdsAdded);
 960  
 
 961  0
         List<String> clientIdsRemoved = (List<String>) getClientIdsRemoved(uiViewRoot);
 962  0
         if (clientIdsRemoved == null)
 963  
         {
 964  
             //Create a set that preserve insertion order
 965  0
             clientIdsRemoved = new ArrayList<String>();
 966  
         }
 967  
 
 968  0
         clientIdsRemoved.add(clientId);
 969  
 
 970  0
         setClientsIdsRemoved(uiViewRoot, clientIdsRemoved);
 971  0
     }
 972  
     
 973  
     @SuppressWarnings("unchecked")
 974  
     private void registerOnAddList(FacesContext facesContext, String clientId)
 975  
     {
 976  0
         UIViewRoot uiViewRoot = facesContext.getViewRoot();
 977  
 
 978  0
         List<String> clientIdsAdded = (List<String>) getClientIdsAdded(uiViewRoot);
 979  0
         if (clientIdsAdded == null)
 980  
         {
 981  
             //Create a set that preserve insertion order
 982  0
             clientIdsAdded = new ArrayList<String>();
 983  
         }
 984  0
         clientIdsAdded.add(clientId);
 985  
 
 986  0
         setClientsIdsAdded(uiViewRoot, clientIdsAdded);
 987  0
     }
 988  
 
 989  
     private void saveStateOnMapVisitTree(final FacesContext facesContext, final Map<String,Object> states,
 990  
             final UIViewRoot uiViewRoot)
 991  
     {
 992  0
         facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
 993  
         try
 994  
         {
 995  0
             uiViewRoot.visitTree( getVisitContextFactory().getVisitContext(
 996  
                     facesContext, null, VISIT_HINTS), new VisitCallback()
 997  0
             {
 998  
                 public VisitResult visit(VisitContext context, UIComponent target)
 999  
                 {
 1000  0
                     FacesContext facesContext = context.getFacesContext();
 1001  
                     Object state;
 1002  
                     
 1003  0
                     if ((target == null) || target.isTransient())
 1004  
                     {
 1005  
                         // No need to bother with these components or their children.
 1006  
                         
 1007  0
                         return VisitResult.REJECT;
 1008  
                     }
 1009  
                     
 1010  0
                     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  0
                     if (componentAddedAfterBuildView != null && (target.getParent() != null))
 1015  
                     {
 1016  0
                         if (ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
 1017  
                         {
 1018  0
                             registerOnAddRemoveList(facesContext, target.getClientId(facesContext));
 1019  0
                             target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
 1020  
                         }
 1021  0
                         else if (ComponentState.ADD.equals(componentAddedAfterBuildView))
 1022  
                         {
 1023  0
                             registerOnAddList(facesContext, target.getClientId(facesContext));
 1024  0
                             target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
 1025  
                         }
 1026  0
                         else if (ComponentState.ADDED.equals(componentAddedAfterBuildView))
 1027  
                         {
 1028  0
                             registerOnAddList(facesContext, target.getClientId(facesContext));
 1029  
                         }
 1030  0
                         ensureClearInitialState(target);
 1031  
                         //Save all required info to restore the subtree.
 1032  
                         //This includes position, structure and state of subtree
 1033  
                         
 1034  0
                         int childIndex = target.getParent().getChildren().indexOf(target);
 1035  0
                         if (childIndex >= 0)
 1036  
                         {
 1037  0
                             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  0
                             String facetName = null;
 1048  0
                             if (target.getParent().getFacetCount() > 0)
 1049  
                             {
 1050  0
                                 for (Map.Entry<String, UIComponent> entry : target.getParent().getFacets().entrySet()) 
 1051  
                                 {
 1052  0
                                     if (target.equals(entry.getValue()))
 1053  
                                     {
 1054  0
                                         facetName = entry.getKey();
 1055  0
                                         break;
 1056  
                                     }
 1057  0
                                 }
 1058  
                             }
 1059  0
                             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  0
                         return VisitResult.REJECT;
 1067  
                     }
 1068  0
                     else if (target.getParent() != null)
 1069  
                     {
 1070  0
                         state = target.saveState (facesContext);
 1071  
                         
 1072  0
                         if (state != null)
 1073  
                         {
 1074  
                             // Save by client ID into our map.
 1075  
                             
 1076  0
                             states.put (target.getClientId (facesContext), state);
 1077  
                         }
 1078  
                         
 1079  0
                         return VisitResult.ACCEPT;
 1080  
                     }
 1081  
                     else
 1082  
                     {
 1083  
                         //Only UIViewRoot has no parent in a component tree.
 1084  0
                         return VisitResult.ACCEPT;
 1085  
                     }
 1086  
                 }
 1087  
             });
 1088  
         }
 1089  
         finally
 1090  
         {
 1091  0
             facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
 1092  0
         }
 1093  0
         if (!uiViewRoot.isTransient())
 1094  
         {
 1095  0
             Object state = uiViewRoot.saveState (facesContext);
 1096  0
             if (state != null)
 1097  
             {
 1098  
                 // Save by client ID into our map.
 1099  
 
 1100  0
                 states.put (uiViewRoot.getClientId (facesContext), state);
 1101  
             }
 1102  
         }
 1103  0
     }
 1104  
     
 1105  
     
 1106  
     private SaveStateAndResetViewCallback saveStateOnMapVisitTreeAndReset(final FacesContext facesContext,
 1107  
             final Map<String,Object> states, final UIViewRoot uiViewRoot, boolean forceHardReset)
 1108  
     {
 1109  0
         facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
 1110  0
         SaveStateAndResetViewCallback callback = new SaveStateAndResetViewCallback(
 1111  
                 facesContext.getViewRoot(), states, forceHardReset);
 1112  0
         if (forceHardReset)
 1113  
         {
 1114  0
             uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
 1115  
                     ViewPoolProcessor.RESET_MODE_HARD);
 1116  
         }
 1117  
         else
 1118  
         {
 1119  0
             uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
 1120  
                     ViewPoolProcessor.RESET_MODE_SOFT);
 1121  
         }
 1122  
         try
 1123  
         {
 1124  0
             if (_viewPoolProcessor != null && 
 1125  
                 !_viewPoolProcessor.isViewPoolEnabledForThisView(facesContext, uiViewRoot))
 1126  
             {
 1127  0
                 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  0
             if (callback.isViewResetable())
 1134  
             {
 1135  0
                 List<String> removedIds = getClientIdsRemoved(uiViewRoot);
 1136  0
                 if (removedIds != null && !removedIds.isEmpty())
 1137  
                 {
 1138  0
                     callback.setViewResetable(false);
 1139  
                 }
 1140  
             }
 1141  
 
 1142  
             try
 1143  
             {
 1144  0
                 uiViewRoot.visitTree( getVisitContextFactory().getVisitContext(
 1145  
                         facesContext, null, VISIT_HINTS), callback);
 1146  
             }
 1147  
             finally
 1148  
             {
 1149  0
                 facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
 1150  0
             }
 1151  
             
 1152  0
             if (callback.isViewResetable() && callback.isRemoveAddedComponents())
 1153  
             {
 1154  0
                 List<String> clientIdsToRemove = getClientIdsAdded(uiViewRoot);
 1155  
 
 1156  0
                 if (clientIdsToRemove != null)
 1157  
                 {
 1158  
                     // perf: clientIds are ArrayList: see method registerOnAddRemoveList(String)
 1159  0
                     for (int i = 0, size = clientIdsToRemove.size(); i < size; i++)
 1160  
                     {
 1161  0
                         String clientId = clientIdsToRemove.get(i);
 1162  0
                         uiViewRoot.invokeOnComponent(facesContext, clientId, new RemoveComponentCallback());
 1163  
                     }
 1164  
                 }
 1165  
             }
 1166  
 
 1167  0
             Object state = uiViewRoot.saveState (facesContext);
 1168  0
             if (state != null)
 1169  
             {
 1170  
                 // Save by client ID into our map.
 1171  0
                 states.put (uiViewRoot.getClientId (facesContext), state);
 1172  
 
 1173  
                 //Hard reset (or reset and check state again)
 1174  0
                 Integer oldResetMode = (Integer) uiViewRoot.getAttributes().put(
 1175  
                         ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, ViewPoolProcessor.RESET_MODE_HARD);
 1176  0
                 state = uiViewRoot.saveState (facesContext);
 1177  0
                 uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, oldResetMode);
 1178  0
                 if (state != null)
 1179  
                 {
 1180  0
                     callback.setViewResetable(false);
 1181  
                 }
 1182  
             }
 1183  
         }
 1184  
         finally
 1185  
         {
 1186  0
             uiViewRoot.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
 1187  
                     ViewPoolProcessor.RESET_MODE_OFF);
 1188  0
         }
 1189  0
         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  0
         {
 1211  0
             this.states = states;
 1212  0
             this.view = view;
 1213  0
             this.viewResetable = true;
 1214  0
             this.skipRoot = true;
 1215  0
             this.count = 0;
 1216  0
             this.forceHardReset = forceHardReset;
 1217  0
             this.removeAddedComponents = false;
 1218  0
         }
 1219  
         
 1220  
         public VisitResult visit(VisitContext context, UIComponent target)
 1221  
         {
 1222  0
             FacesContext facesContext = context.getFacesContext();
 1223  
             Object state;
 1224  0
             this.count++;
 1225  
 
 1226  0
             if ((target == null) || target.isTransient())
 1227  
             {
 1228  
                 // No need to bother with these components or their children.
 1229  
 
 1230  0
                 return VisitResult.REJECT;
 1231  
             }
 1232  
             
 1233  0
             if (skipRoot && target instanceof UIViewRoot)
 1234  
             {
 1235  
                 //UIViewRoot should be scanned at last.
 1236  0
                 skipRoot = false;
 1237  0
                 return VisitResult.ACCEPT;
 1238  
             }
 1239  
 
 1240  0
             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  0
             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  0
                 setRemoveAddedComponents(true);
 1250  0
                 if (forceHardReset)
 1251  
                 {
 1252  
                     // The ideal is remove the added component here but visitTree does not support that
 1253  
                     // kind of tree manipulation.
 1254  0
                     if (isViewResetable() &&
 1255  
                         ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
 1256  
                     {
 1257  0
                         setViewResetable(false);
 1258  
                     }
 1259  
                     // it is not important to save anything, skip
 1260  0
                     return VisitResult.REJECT;
 1261  
                 }
 1262  0
                 if (ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
 1263  
                 {
 1264  
                     //If the view has removed components, set the view as non resetable
 1265  0
                     setViewResetable(false);
 1266  0
                     registerOnAddRemoveList(facesContext, target.getClientId(facesContext));
 1267  0
                     target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
 1268  
                 }
 1269  0
                 else if (ComponentState.ADD.equals(componentAddedAfterBuildView))
 1270  
                 {
 1271  0
                     registerOnAddList(facesContext, target.getClientId(facesContext));
 1272  0
                     target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
 1273  
                 }
 1274  0
                 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  0
                     registerOnAddList(facesContext, target.getClientId(facesContext));
 1279  
                 }
 1280  0
                 ensureClearInitialState(target);
 1281  
                 //Save all required info to restore the subtree.
 1282  
                 //This includes position, structure and state of subtree
 1283  
 
 1284  0
                 int childIndex = target.getParent().getChildren().indexOf(target);
 1285  0
                 if (childIndex >= 0)
 1286  
                 {
 1287  0
                     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  0
                     String facetName = null;
 1298  0
                     if (target.getParent().getFacetCount() > 0)
 1299  
                     {
 1300  0
                         for (Map.Entry<String, UIComponent> entry : target.getParent().getFacets().entrySet()) 
 1301  
                         {
 1302  0
                             if (target.equals(entry.getValue()))
 1303  
                             {
 1304  0
                                 facetName = entry.getKey();
 1305  0
                                 break;
 1306  
                             }
 1307  0
                         }
 1308  
                     }
 1309  0
                     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  0
                 return VisitResult.REJECT;
 1317  
             }
 1318  0
             else if (target.getParent() != null)
 1319  
             {
 1320  0
                 if (forceHardReset)
 1321  
                 {
 1322  
                     // force hard reset set reset move on top
 1323  0
                     state = target.saveState (facesContext);
 1324  0
                     if (state != null)
 1325  
                     {
 1326  0
                         setViewResetable(false);
 1327  0
                         return VisitResult.REJECT;
 1328  
                     }
 1329  
                 }
 1330  
                 else
 1331  
                 {
 1332  0
                     state = target.saveState (facesContext);
 1333  
 
 1334  0
                     if (state != null)
 1335  
                     {
 1336  
                         // Save by client ID into our map.
 1337  0
                         states.put (target.getClientId (facesContext), state);
 1338  
 
 1339  0
                         if (isViewResetable())
 1340  
                         {
 1341  
                             //Hard reset (or reset and check state again)
 1342  0
                             Integer oldResetMode = (Integer) view.getAttributes().put(
 1343  
                                     ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
 1344  
                                     ViewPoolProcessor.RESET_MODE_HARD);
 1345  0
                             state = target.saveState (facesContext);
 1346  0
                             view.getAttributes().put(ViewPoolProcessor.RESET_SAVE_STATE_MODE_KEY, 
 1347  
                                     oldResetMode);
 1348  0
                             if (state != null)
 1349  
                             {
 1350  0
                                 setViewResetable(false);
 1351  
                             }
 1352  
                         }
 1353  
                     }
 1354  
                 }
 1355  
 
 1356  0
                 return VisitResult.ACCEPT;
 1357  
             }
 1358  
             else
 1359  
             {
 1360  
                 //Only UIViewRoot has no parent in a component tree.
 1361  0
                 return VisitResult.ACCEPT;
 1362  
             }
 1363  
         }
 1364  
         
 1365  
         /**
 1366  
          * @return the viewResetable
 1367  
          */
 1368  
         public boolean isViewResetable()
 1369  
         {
 1370  0
             return viewResetable;
 1371  
         }
 1372  
 
 1373  
         /**
 1374  
          * @param viewResetable the viewResetable to set
 1375  
          */
 1376  
         public void setViewResetable(boolean viewResetable)
 1377  
         {
 1378  0
             this.viewResetable = viewResetable;
 1379  0
         }
 1380  
         
 1381  
         public int getCount()
 1382  
         {
 1383  0
             return count;
 1384  
         }
 1385  
 
 1386  
         /**
 1387  
          * @return the removeAddedComponents
 1388  
          */
 1389  
         public boolean isRemoveAddedComponents()
 1390  
         {
 1391  0
             return removeAddedComponents;
 1392  
         }
 1393  
 
 1394  
         /**
 1395  
          * @param removeAddedComponents the removeAddedComponents to set
 1396  
          */
 1397  
         public void setRemoveAddedComponents(boolean removeAddedComponents)
 1398  
         {
 1399  0
             this.removeAddedComponents = removeAddedComponents;
 1400  0
         }
 1401  
     }
 1402  
     
 1403  
     protected void ensureClearInitialState(UIComponent c)
 1404  
     {
 1405  0
         c.clearInitialState();
 1406  0
         if (c.getChildCount() > 0)
 1407  
         {
 1408  0
             for (int i = 0, childCount = c.getChildCount(); i < childCount; i++)
 1409  
             {
 1410  0
                 UIComponent child = c.getChildren().get(i);
 1411  0
                 ensureClearInitialState(child);
 1412  
             }
 1413  
         }
 1414  0
         if (c.getFacetCount() > 0)
 1415  
         {
 1416  0
             for (UIComponent child : c.getFacets().values())
 1417  
             {
 1418  0
                 ensureClearInitialState(child);
 1419  0
             }
 1420  
         }
 1421  0
     }
 1422  
     
 1423  
     public void suscribeListeners(UIViewRoot uiViewRoot)
 1424  
     {
 1425  0
         boolean listenerSubscribed = false;
 1426  0
         List<SystemEventListener> pavList = uiViewRoot.getViewListenersForEventClass(PostAddToViewEvent.class);
 1427  0
         if (pavList != null)
 1428  
         {
 1429  0
             for (SystemEventListener listener : pavList)
 1430  
             {
 1431  0
                 if (listener instanceof PostAddPreRemoveFromViewListener)
 1432  
                 {
 1433  0
                     listenerSubscribed = true;
 1434  0
                     break;
 1435  
                 }
 1436  0
             }
 1437  
         }
 1438  0
         if (!listenerSubscribed)
 1439  
         {
 1440  0
             PostAddPreRemoveFromViewListener componentListener = new PostAddPreRemoveFromViewListener();
 1441  0
             uiViewRoot.subscribeToViewEvent(PostAddToViewEvent.class, componentListener);
 1442  0
             uiViewRoot.subscribeToViewEvent(PreRemoveFromViewEvent.class, componentListener);
 1443  
         }
 1444  0
     }
 1445  
     
 1446  
     protected RenderKitFactory getRenderKitFactory()
 1447  
     {
 1448  0
         if (_renderKitFactory == null)
 1449  
         {
 1450  0
             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
 1451  
         }
 1452  0
         return _renderKitFactory;
 1453  
     }
 1454  
     
 1455  
     protected VisitContextFactory getVisitContextFactory()
 1456  
     {
 1457  0
         if (_visitContextFactory == null)
 1458  
         {
 1459  0
             _visitContextFactory = (VisitContextFactory)FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY);
 1460  
         }
 1461  0
         return _visitContextFactory;
 1462  
     }
 1463  
 
 1464  
     protected String getCheckIdProductionMode(FacesContext facesContext)
 1465  
     {
 1466  0
         if (_checkIdsProductionMode == null)
 1467  
         {
 1468  0
             _checkIdsProductionMode
 1469  
                     = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
 1470  
                     CHECK_ID_PRODUCTION_MODE, CHECK_ID_PRODUCTION_MODE_DEFAULT); //default (auto)
 1471  
         }
 1472  0
         return _checkIdsProductionMode;
 1473  
     }
 1474  
 
 1475  
     
 1476  0
     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  0
             return (source instanceof UIComponent);
 1487  
         }
 1488  
         
 1489  
         private boolean isRefreshOnTransientBuildPreserveState()
 1490  
         {
 1491  0
             if (_isRefreshOnTransientBuildPreserveState == null)
 1492  
             {
 1493  0
                 _isRefreshOnTransientBuildPreserveState = MyfacesConfig.getCurrentInstance(
 1494  
                         _facesContext.getExternalContext()).isRefreshTransientBuildOnPSSPreserveState();
 1495  
             }
 1496  0
             return _isRefreshOnTransientBuildPreserveState;
 1497  
         }
 1498  
 
 1499  
         public void processEvent(SystemEvent event)
 1500  
         {
 1501  0
             UIComponent component = (UIComponent) event.getSource();
 1502  
             
 1503  0
             if (component.isTransient())
 1504  
             {
 1505  0
                 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  0
             if (_facesContext == null)
 1512  
             {
 1513  0
                 _facesContext = FacesContext.getCurrentInstance();
 1514  
             }
 1515  
             
 1516  0
             if (event instanceof PostAddToViewEvent)
 1517  
             {
 1518  0
                 if (!isRefreshOnTransientBuildPreserveState() &&
 1519  
                     Boolean.TRUE.equals(_facesContext.getAttributes().get(StateManager.IS_BUILDING_INITIAL_STATE)))
 1520  
                 {
 1521  0
                     return;
 1522  
                 }
 1523  
 
 1524  
                 //PostAddToViewEvent
 1525  0
                 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  0
                 if (FaceletViewDeclarationLanguage.isRemovingComponentBuild(_facesContext))
 1535  
                 {
 1536  0
                     return;
 1537  
                 }
 1538  
 
 1539  0
                 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  0
                     return;
 1553  
                 }
 1554  
                 
 1555  
                 //PreRemoveFromViewEvent
 1556  0
                 UIViewRoot uiViewRoot = _facesContext.getViewRoot();
 1557  
                 
 1558  0
                 List<String> clientIdsRemoved = getClientIdsRemoved(uiViewRoot);
 1559  0
                 if (clientIdsRemoved == null)
 1560  
                 {
 1561  
                     //Create a set that preserve insertion order
 1562  0
                     clientIdsRemoved = new ArrayList<String>();
 1563  
                 }
 1564  0
                 clientIdsRemoved.add(component.getClientId(_facesContext));
 1565  0
                 setClientsIdsRemoved(uiViewRoot, clientIdsRemoved);
 1566  
             }
 1567  0
         }
 1568  
     }
 1569  
     
 1570  
     private static TreeStructComponent internalBuildTreeStructureToSave(UIComponent component)
 1571  
     {
 1572  0
         TreeStructComponent structComp = new TreeStructComponent(component.getClass().getName(),
 1573  
                                                                  component.getId());
 1574  
 
 1575  
         //children
 1576  0
         if (component.getChildCount() > 0)
 1577  
         {
 1578  0
             List<TreeStructComponent> structChildList = new ArrayList<TreeStructComponent>();
 1579  0
             for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
 1580  
             {
 1581  0
                 UIComponent child = component.getChildren().get(i);     
 1582  0
                 if (!child.isTransient())
 1583  
                 {
 1584  0
                     TreeStructComponent structChild = internalBuildTreeStructureToSave(child);
 1585  0
                     structChildList.add(structChild);
 1586  
                 }
 1587  
             }
 1588  
             
 1589  0
             TreeStructComponent[] childArray = structChildList.toArray(new TreeStructComponent[structChildList.size()]);
 1590  0
             structComp.setChildren(childArray);
 1591  
         }
 1592  
 
 1593  
         //facets
 1594  
         
 1595  0
         if (component.getFacetCount() > 0)
 1596  
         {
 1597  0
             Map<String, UIComponent> facetMap = component.getFacets();
 1598  0
             List<Object[]> structFacetList = new ArrayList<Object[]>();
 1599  0
             for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
 1600  
             {
 1601  0
                 UIComponent child = entry.getValue();
 1602  0
                 if (!child.isTransient())
 1603  
                 {
 1604  0
                     String facetName = entry.getKey();
 1605  0
                     TreeStructComponent structChild = internalBuildTreeStructureToSave(child);
 1606  0
                     structFacetList.add(new Object[] {facetName, structChild});
 1607  
                 }
 1608  0
             }
 1609  
             
 1610  0
             Object[] facetArray = structFacetList.toArray(new Object[structFacetList.size()]);
 1611  0
             structComp.setFacets(facetArray);
 1612  
         }
 1613  
 
 1614  0
         return structComp;
 1615  
     }
 1616  
     
 1617  
     private static UIComponent internalRestoreTreeStructure(TreeStructComponent treeStructComp)
 1618  
     {
 1619  0
         String compClass = treeStructComp.getComponentClass();
 1620  0
         String compId = treeStructComp.getComponentId();
 1621  0
         UIComponent component = (UIComponent)ClassUtils.newInstance(compClass);
 1622  0
         component.setId(compId);
 1623  
 
 1624  
         //children
 1625  0
         TreeStructComponent[] childArray = treeStructComp.getChildren();
 1626  0
         if (childArray != null)
 1627  
         {
 1628  0
             List<UIComponent> childList = component.getChildren();
 1629  0
             for (int i = 0, len = childArray.length; i < len; i++)
 1630  
             {
 1631  0
                 UIComponent child = internalRestoreTreeStructure(childArray[i]);
 1632  0
                 childList.add(child);
 1633  
             }
 1634  
         }
 1635  
 
 1636  
         //facets
 1637  0
         Object[] facetArray = treeStructComp.getFacets();
 1638  0
         if (facetArray != null)
 1639  
         {
 1640  0
             Map<String, UIComponent> facetMap = component.getFacets();
 1641  0
             for (int i = 0, len = facetArray.length; i < len; i++)
 1642  
             {
 1643  0
                 Object[] tuple = (Object[])facetArray[i];
 1644  0
                 String facetName = (String)tuple[0];
 1645  0
                 TreeStructComponent structChild = (TreeStructComponent)tuple[1];
 1646  0
                 UIComponent child = internalRestoreTreeStructure(structChild);
 1647  0
                 facetMap.put(facetName, child);
 1648  
             }
 1649  
         }
 1650  
 
 1651  0
         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  0
         private TreeStructComponent[] _children = null; // Array of children
 1660  0
         private Object[] _facets = null; // Array of Array-tuples with Facetname and TreeStructComponent
 1661  
 
 1662  
         TreeStructComponent(String componentClass, String componentId)
 1663  0
         {
 1664  0
             _componentClass = componentClass;
 1665  0
             _componentId = componentId;
 1666  0
         }
 1667  
 
 1668  
         public String getComponentClass()
 1669  
         {
 1670  0
             return _componentClass;
 1671  
         }
 1672  
 
 1673  
         public String getComponentId()
 1674  
         {
 1675  0
             return _componentId;
 1676  
         }
 1677  
 
 1678  
         void setChildren(TreeStructComponent[] children)
 1679  
         {
 1680  0
             _children = children;
 1681  0
         }
 1682  
 
 1683  
         TreeStructComponent[] getChildren()
 1684  
         {
 1685  0
             return _children;
 1686  
         }
 1687  
 
 1688  
         Object[] getFacets()
 1689  
         {
 1690  0
             return _facets;
 1691  
         }
 1692  
 
 1693  
         void setFacets(Object[] facets)
 1694  
         {
 1695  0
             _facets = facets;
 1696  0
         }
 1697  
     }
 1698  
     
 1699  
 }