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.Collection;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  
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.UIViewParameter;
39  import javax.faces.component.UIViewRoot;
40  import javax.faces.component.visit.VisitCallback;
41  import javax.faces.component.visit.VisitContext;
42  import javax.faces.component.visit.VisitContextFactory;
43  import javax.faces.component.visit.VisitHint;
44  import javax.faces.component.visit.VisitResult;
45  import javax.faces.context.ExternalContext;
46  import javax.faces.context.FacesContext;
47  import javax.faces.event.PostAddToViewEvent;
48  import javax.faces.event.PreRemoveFromViewEvent;
49  import javax.faces.event.SystemEvent;
50  import javax.faces.event.SystemEventListener;
51  import javax.faces.render.RenderKitFactory;
52  import javax.faces.render.ResponseStateManager;
53  import javax.faces.view.StateManagementStrategy;
54  import javax.faces.view.ViewDeclarationLanguage;
55  import javax.faces.view.ViewDeclarationLanguageFactory;
56  import javax.faces.view.ViewMetadata;
57  
58  import org.apache.myfaces.application.StateManagerImpl;
59  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
60  import org.apache.myfaces.context.RequestViewContext;
61  import org.apache.myfaces.shared.config.MyfacesConfig;
62  import org.apache.myfaces.shared.util.ClassUtils;
63  import org.apache.myfaces.shared.util.HashMapUtils;
64  import org.apache.myfaces.shared.util.WebConfigParamUtils;
65  import org.apache.myfaces.view.facelets.compiler.CheckDuplicateIdFaceletUtils;
66  import org.apache.myfaces.view.facelets.tag.jsf.ComponentSupport;
67  
68  /**
69   * This class implements partial state saving feature when facelets
70   * is used to render pages. (Theorically it could be applied on jsp case too,
71   * but all considerations below should be true before apply it).
72   * 
73   * The following considerations apply for this class:
74   * 
75   * 1. This StateManagementStrategy should only be active if javax.faces.PARTIAL_STATE_SAVING
76   *    config param is active(true). See javadoc on StateManager for details.
77   * 2. A map using component clientId as keys are used to hold the state.
78   * 3. Each component has a valid id after ViewDeclarationLanguage.buildView().
79   *    This implies that somewhere, every TagHandler that create an UIComponent 
80   *    instance should call setId and assign it.
81   * 4. Every TagHandler that create an UIComponent instance should call markInitialState
82   *    after the component is populated. Otherwise, full state is always saved.
83   * 5. A SystemEventListener is used to keep track for added and removed components, listen
84   *    PostAddToViewEvent and PreRemoveFromViewEvent event triggered by UIComponent.setParent()
85   *    method.
86   * 6. It is not possible to use javax.faces.component.visit API to traverse the component
87   *    tree during save/restore, because UIData.visitTree traverse all rows and we only need
88   *    to restore state per component (not per row).
89   * 7. It is necessary to preserve the order of the children added/removed between requests.
90   * 8. Added and removed components could be seen as subtrees. This imply that we need to save
91   *    the structure of the added components subtree and remove one component could be remove
92   *    all its children and facets from view inclusive.
93   * 9. It is necessary to save and restore the list of added/removed components between several
94   *    requests.
95   * 10.All components ids removed in any moment of time must be preserved.
96   * 11.Each component must be restored only once.
97   * 11.The order is important for ids added when it is traversed the tree, otherwise the algorithm 
98   *    could change the order in which components will be restored.  
99   * 
100  * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
101  * @version $Revision: 1505865 $ $Date: 2013-07-22 19:19:31 -0500 (Mon, 22 Jul 2013) $
102  * @since 2.0
103  *
104  */
105 public class DefaultFaceletsStateManagementStrategy extends StateManagementStrategy
106 {
107     public static final String CLIENTIDS_ADDED = "oam.CLIENTIDS_ADDED";
108     
109     public static final String CLIENTIDS_REMOVED = "oam.CLIENTIDS_REMOVED";
110     
111     /**
112      * Key used on component attribute map to indicate if a component was added
113      * after build view, so itself and all descendants should not use partial
114      * state saving. There are two possible values:
115      * 
116      * Key not present: The component uses pss.
117      * ComponentState.ADD: The component was added to the view after build view.
118      * ComponentState.REMOVE_ADD: The component was removed/added to the view. Itself and all
119      * descendants should be saved and restored, but we have to unregister/register
120      * from CLIENTIDS_ADDED and CLIENTIDS_REMOVED lists. See ComponentSupport.markComponentToRestoreFully
121      * for details.
122      * ComponentState.ADDED: The component has been added or removed/added, but it has
123      * been already processed.
124      */
125     public  static final String COMPONENT_ADDED_AFTER_BUILD_VIEW = "oam.COMPONENT_ADDED_AFTER_BUILD_VIEW"; 
126     
127     /**
128      * If this param is set to true (by default), when pss algorithm is executed to save state, a visit tree
129      * traversal is done, instead a plain traversal like previous versions (2.0.7/2.1.1 and earlier) of MyFaces Core.
130      * 
131      * This param is just provided to preserve backwards behavior. 
132      */
133     @JSFWebConfigParam(since="2.0.8, 2.1.2", defaultValue="true", expectedValues="true, false",
134                        group="state", tags="performance")
135     public static final String SAVE_STATE_WITH_VISIT_TREE_ON_PSS
136             = "org.apache.myfaces.SAVE_STATE_WITH_VISIT_TREE_ON_PSS";
137     
138     /**
139      * Define how duplicate ids are checked when ProjectStage is Production, by default (auto) it only check ids of
140      * components that does not encapsulate markup (like facelets UILeaf).
141      *  
142      * <ul>
143      * <li>true: check all ids, including ids for components that are transient and encapsulate markup.</li>
144      * <li>auto: (default) check ids of components that does not encapsulate markup (like facelets UILeaf). 
145      * Note ids of UILeaf instances are generated by facelets vdl, start with "j_id", are never rendered 
146      * into the response and UILeaf instances are never used as a target for listeners, so in practice 
147      * there is no need to check such ids. This reduce the overhead associated with generate client ids.</li>
148      * <li>false: do not do any check when ProjectStage is Production</li>
149      * </ul>
150      * <p> According to specification, identifiers must be unique within the scope of the nearest ancestor to 
151      * the component that is a naming container.</p>
152      */
153     @JSFWebConfigParam(since="2.0.12, 2.1.6", defaultValue="auto", expectedValues="true, auto, false",
154                        group="state", tags="performance")
155     public static final String CHECK_ID_PRODUCTION_MODE
156             = "org.apache.myfaces.CHECK_ID_PRODUCTION_MODE";
157     
158     private static final String CHECK_ID_PRODUCTION_MODE_DEFAULT = "auto";
159     private static final String CHECK_ID_PRODUCTION_MODE_TRUE = "true";
160     private static final String CHECK_ID_PRODUCTION_MODE_FALSE = "false";
161     private static final String CHECK_ID_PRODUCTION_MODE_AUTO = "auto";
162     
163     private static final String SKIP_ITERATION_HINT = "javax.faces.visit.SKIP_ITERATION";
164     
165     private static final String SERIALIZED_VIEW_REQUEST_ATTR = 
166         StateManagerImpl.class.getName() + ".SERIALIZED_VIEW";
167     
168     private static final Object[] EMPTY_STATES = new Object[]{null, null};
169     
170     private static final String UNIQUE_ID_COUNTER_KEY =
171               "oam.view.uniqueIdCounter";
172     
173     private ViewDeclarationLanguageFactory _vdlFactory;
174     
175     private RenderKitFactory _renderKitFactory = null;
176     
177     private VisitContextFactory _visitContextFactory = null;
178     
179     private Boolean _saveStateWithVisitTreeOnPSS;
180     
181     private String _checkIdsProductionMode;
182     
183     public DefaultFaceletsStateManagementStrategy ()
184     {
185         _vdlFactory = (ViewDeclarationLanguageFactory)
186                 FactoryFinder.getFactory(FactoryFinder.VIEW_DECLARATION_LANGUAGE_FACTORY);
187         //TODO: This object should be application scoped and shared
188         //between jsp and facelets
189     }
190     
191     @SuppressWarnings("unchecked")
192     @Override
193     public UIViewRoot restoreView (FacesContext context, String viewId, String renderKitId)
194     {
195         ResponseStateManager manager;
196         Object state[];
197         Map<String, Object> states;
198         UIViewRoot view = null;
199  
200         // The value returned here is expected to be false (set by RestoreViewExecutor), but
201         //we don't know if some ViewHandler wrapper could change it, so it is better to save the value.
202         final boolean oldContextEventState = context.isProcessingEvents();
203         // Get previous state from ResponseStateManager.
204         manager = getRenderKitFactory().getRenderKit(context, renderKitId).getResponseStateManager();
205         
206         state = (Object[]) manager.getState(context, viewId);
207         
208         if (state == null)
209         {
210             //No state could be restored, return null causing ViewExpiredException
211             return null;
212         }
213         
214         if (state[1] instanceof Object[])
215         {
216             Object[] fullState = (Object[]) state[1]; 
217             view = (UIViewRoot) internalRestoreTreeStructure((TreeStructComponent)fullState[0]);
218 
219             if (view != null)
220             {
221                 context.setViewRoot (view);
222                 view.processRestoreState(context, fullState[1]);
223                 
224                 // If the view is restored fully, it is necessary to refresh RequestViewContext, otherwise at
225                 // each ajax request new components associated with @ResourceDependency annotation will be added
226                 // to the tree, making the state bigger without real need.
227                 RequestViewContext.getCurrentInstance(context).
228                         refreshRequestViewContext(context, view);
229             }
230         }
231         else
232         {
233             // Per the spec: build the view.
234             ViewDeclarationLanguage vdl = _vdlFactory.getViewDeclarationLanguage(viewId);
235             Object faceletViewState = null;
236             try
237             {
238                 ViewMetadata metadata = vdl.getViewMetadata (context, viewId);
239                 
240                 Collection<UIViewParameter> viewParameters = null;
241                 
242                 if (metadata != null)
243                 {
244                     view = metadata.createMetadataView(context);
245                     
246                     if (view != null)
247                     {
248                         viewParameters = metadata.getViewParameters(view);
249                     }
250                 }
251                 if (view == null)
252                 {
253                     view = context.getApplication().getViewHandler().createView(context, viewId);
254                 }
255                 
256                 context.setViewRoot (view); 
257                 
258                 if (state != null && state[1] != null)
259                 {
260                     states = (Map<String, Object>) state[1];
261                     faceletViewState = UIComponentBase.restoreAttachedState(
262                             context,states.get(ComponentSupport.FACELET_STATE_INSTANCE));
263                     if (faceletViewState != null)
264                     {
265                         view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
266                     }
267                     if (state.length == 3)
268                     {
269                         if (view.getId() == null)
270                         {
271                             view.setId(view.createUniqueId(context, null));
272                         }
273                         //Jump to where the count is
274                         view.getAttributes().put(UNIQUE_ID_COUNTER_KEY, state[2]);
275                     }
276                 }
277                 // TODO: Why is necessary enable event processing?
278                 // ANS: On RestoreViewExecutor, setProcessingEvents is called first to false
279                 // and then to true when postback. Since we need listeners registered to PostAddToViewEvent
280                 // event to be handled, we should enable it again. We are waiting a response from EG about
281                 // the behavior of those listeners, because for partial state saving we need this listeners
282                 // be called from here and relocate components properly, but for now we have to let this code as is.
283                 try 
284                 {
285                     context.setProcessingEvents (true);
286                     vdl.buildView (context, view);
287                     // In the latest code related to PostAddToView, it is
288                     // triggered no matter if it is applied on postback. It seems that MYFACES-2389, 
289                     // TRINIDAD-1670 and TRINIDAD-1671 are related.
290                     // This code is no longer necessary, but better let it here.
291                     //_publishPostBuildComponentTreeOnRestoreViewEvent(context, view);
292                     suscribeListeners(view);
293                 }
294                 finally
295                 {
296                     context.setProcessingEvents (oldContextEventState);
297                 }
298             }
299             catch (Throwable e)
300             {
301                 throw new FacesException ("unable to create view \"" + viewId + "\"", e);
302             }
303 
304             if (state != null && state[1] != null)
305             {
306                 states = (Map<String, Object>) state[1];
307                 //Save the last unique id counter key in UIViewRoot
308                 Long lastUniqueIdCounter = (Long) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
309                 
310                 // Visit the children and restore their state.
311                 boolean emptyState = false;
312                 boolean containsFaceletState = states.containsKey(
313                         ComponentSupport.FACELET_STATE_INSTANCE);
314                 if (states.isEmpty())
315                 {
316                     emptyState = true; 
317                 }
318                 else if (states.size() == 1 && 
319                         containsFaceletState)
320                 {
321                     emptyState = true; 
322                 }
323                 //Restore state of current components
324                 if (!emptyState)
325                 {
326                     // Check if there is only one component state
327                     // and that state is UIViewRoot instance (for example
328                     // when using ViewScope)
329                     if ((states.size() == 1 && !containsFaceletState) || 
330                         (states.size() == 2 && containsFaceletState))
331                     {
332                         Object viewState = states.get(view.getClientId(context));
333                         if (viewState != null)
334                         {
335                             restoreViewRootOnlyFromMap(context,viewState, view);
336                         }
337                         else
338                         {
339                             //The component is not viewRoot, restore as usual.
340                             restoreStateFromMap(context, states, view);
341                         }
342                     }
343                     else
344                     {
345                         restoreStateFromMap(context, states, view);
346                     }
347                 }
348                 if (faceletViewState != null)
349                 {
350                     view.getAttributes().put(ComponentSupport.FACELET_STATE_INSTANCE,  faceletViewState);
351                 }
352                 if (lastUniqueIdCounter != null)
353                 {
354                     Long newUniqueIdCounter = (Long) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
355                     if (newUniqueIdCounter != null && 
356                         lastUniqueIdCounter.longValue() > newUniqueIdCounter.longValue())
357                     {
358                         // The unique counter was restored by a side effect of 
359                         // restoreState() over UIViewRoot with a lower count,
360                         // to avoid a component duplicate id exception we need to fix the count.
361                         view.getAttributes().put(UNIQUE_ID_COUNTER_KEY, lastUniqueIdCounter);
362                     }
363                 }
364                 handleDynamicAddedRemovedComponents(context, view, states);
365             }
366         }
367         // Restore binding, because UIViewRoot.processRestoreState() is never called
368         //the event processing has to be enabled because of the restore view event triggers
369         //TODO ask the EG the this is a spec violation if we do it that way
370         //see Section 2.2.1
371         // TODO: Why is necessary enable event processing?
372         // ANS: On RestoreViewExecutor, setProcessingEvents is called first to false
373         // and then to true when postback. Since we need listeners registered to PostAddToViewEvent
374         // event to be handled, we should enable it again. We are waiting a response from EG about
375         // the behavior of those listeners (see comment on vdl.buildView). 
376         // -= Leonardo Uribe =- I think enable event processing in this point does not have any
377         // side effect. Enable it allows programatically add components when binding is set with 
378         // pss enabled. That feature works without pss, so we should preserve backward behavior.
379         // Tomahawk t:aliasBean example creating components on binding requires this to work.
380         //context.setProcessingEvents(true);
381         //try {
382         //    view.visitTree(VisitContext.createVisitContext(context), new RestoreStateCallback());
383         //} finally {
384         //    context.setProcessingEvents(oldContextEventState);
385         //}
386         return view;
387     }
388     
389     public void handleDynamicAddedRemovedComponents(FacesContext context, UIViewRoot view, Map<String, Object> states)
390     {
391         List<String> clientIdsRemoved = getClientIdsRemoved(view);
392 
393         if (clientIdsRemoved != null)
394         {
395             Set<String> idsRemovedSet = new HashSet<String>(HashMapUtils.calcCapacity(clientIdsRemoved.size()));
396             context.getAttributes().put(FaceletViewDeclarationLanguage.REMOVING_COMPONENTS_BUILD, Boolean.TRUE);
397             try
398             {
399                 // perf: clientIds are ArrayList: see method registerOnAddRemoveList(String)
400                 for (int i = 0, size = clientIdsRemoved.size(); i < size; i++)
401                 {
402                     String clientId = clientIdsRemoved.get(i);
403                     if (!idsRemovedSet.contains(clientId))
404                     {
405                         view.invokeOnComponent(context, clientId, new RemoveComponentCallback());
406                         idsRemovedSet.add(clientId);
407                     }
408                 }
409                 clientIdsRemoved.clear();
410                 clientIdsRemoved.addAll(idsRemovedSet);
411             }
412             finally
413             {
414                 context.getAttributes().remove(FaceletViewDeclarationLanguage.REMOVING_COMPONENTS_BUILD);
415             }
416         }
417         List<String> clientIdsAdded = getClientIdsAdded(view);
418         if (clientIdsAdded != null)
419         {
420             Set<String> idsAddedSet = new HashSet<String>(HashMapUtils.calcCapacity(clientIdsAdded.size()));
421             // perf: clientIds are ArrayList: see method setClientsIdsAdded(String)
422             for (int i = 0, size = clientIdsAdded.size(); i < size; i++)
423             {
424                 String clientId = clientIdsAdded.get(i);
425                 if (!idsAddedSet.contains(clientId))
426                 {
427                     final AttachedFullStateWrapper wrapper = (AttachedFullStateWrapper) states.get(clientId);
428                     if (wrapper != null)
429                     {
430                         final Object[] addedState = (Object[]) wrapper.getWrappedStateObject(); 
431                         if (addedState != null)
432                         {
433                             if (addedState.length == 2)
434                             {
435                                 view = (UIViewRoot)
436                                         internalRestoreTreeStructure((TreeStructComponent) addedState[0]);
437                                 view.processRestoreState(context, addedState[1]);
438                                 break;
439                             }
440                             else
441                             {
442                                 final String parentClientId = (String) addedState[0];
443                                 view.invokeOnComponent(context, parentClientId, 
444                                     new AddComponentCallback(addedState));
445                             }
446                         }
447                     }
448                     idsAddedSet.add(clientId);
449                 }
450             }
451             // Reset this list, because it will be calculated later when the view is being saved
452             // in the right order, preventing duplicates (see COMPONENT_ADDED_AFTER_BUILD_VIEW for details).
453             clientIdsAdded.clear();
454             
455             // This call only has sense when components has been added programatically, because if facelets has control
456             // over all components in the component tree, build the initial state and apply the state will have the
457             // same effect.
458             RequestViewContext.getCurrentInstance(context).
459                     refreshRequestViewContext(context, view);
460         }
461     }
462 
463     public static class RemoveComponentCallback implements ContextCallback
464     {
465         public void invokeContextCallback(FacesContext context,
466                 UIComponent target)
467         {
468             if (target.getParent() != null)
469             {
470                 if (!target.getParent().getChildren().remove(target))
471                 {
472                     String key = null;
473                     if (target.getParent().getFacetCount() > 0)
474                     {
475                         for (Map.Entry<String, UIComponent> entry :
476                                 target.getParent().getFacets().entrySet())
477                         {
478                             if (entry.getValue()==target)
479                             {
480                                 key = entry.getKey();
481                                 break;
482                             }
483                         }
484                     }
485                     if (key != null)
486                     {
487                         target.getParent().getFacets().remove(key);
488                     }
489                 }
490             }
491         }
492     }
493 
494     public static class AddComponentCallback implements ContextCallback
495     {
496         private final Object[] addedState;
497         
498         public AddComponentCallback(Object[] addedState)
499         {
500             this.addedState = addedState;
501         }
502         
503         public void invokeContextCallback(FacesContext context,
504                 UIComponent target)
505         {
506             if (addedState[1] != null)
507             {
508                 String facetName = (String) addedState[1];
509                 UIComponent child
510                         = internalRestoreTreeStructure((TreeStructComponent)
511                                                        addedState[3]);
512                 child.processRestoreState(context, addedState[4]);
513                 target.getFacets().put(facetName,child);
514             }
515             else
516             {
517                 Integer childIndex = (Integer) addedState[2];
518                 UIComponent child
519                         = internalRestoreTreeStructure((TreeStructComponent)
520                                                        addedState[3]);
521                 child.processRestoreState(context, addedState[4]);
522                 try
523                 {
524                     target.getChildren().add(childIndex, child);
525                 }
526                 catch (IndexOutOfBoundsException e)
527                 {
528                     // We can't be sure about where should be this 
529                     // item, so just add it. 
530                     target.getChildren().add(child);
531                 }
532             }
533         }
534     }
535 
536     @Override
537     public Object saveView (FacesContext context)
538     {
539         UIViewRoot view = context.getViewRoot();
540         Object states;
541         
542         if (view == null)
543         {
544             // Not much that can be done.
545             
546             return null;
547         }
548         
549         if (view.isTransient())
550         {
551             // Must return null immediately per spec.
552             
553             return null;
554         }
555         
556         ExternalContext externalContext = context.getExternalContext();
557         
558         Object serializedView = context.getAttributes()
559             .get(SERIALIZED_VIEW_REQUEST_ATTR);
560         
561         //Note on ajax case the method saveState could be called twice: once before start
562         //document rendering and the other one when it is called StateManager.getViewState method.
563         if (serializedView == null)
564         {
565                     
566             // Make sure the client IDs are unique per the spec.
567             
568             if (context.isProjectStage(ProjectStage.Production))
569             {
570                 if (CHECK_ID_PRODUCTION_MODE_AUTO.equals(getCheckIdProductionMode(context)))
571                 {
572                     CheckDuplicateIdFaceletUtils.checkIdsStatefulComponents(context, view);
573                 }
574                 else if (CHECK_ID_PRODUCTION_MODE_TRUE.equals(getCheckIdProductionMode(context)))
575                 {
576                     CheckDuplicateIdFaceletUtils.checkIds(context, view);
577                 }
578             }
579             else
580             {
581                 CheckDuplicateIdFaceletUtils.checkIds(context, view);
582             }
583             
584             // Create save state objects for every component.
585             
586             //view.visitTree (VisitContext.createVisitContext (context), new SaveStateVisitor (states));
587             
588             if (view.getAttributes().containsKey(COMPONENT_ADDED_AFTER_BUILD_VIEW))
589             {
590                 ensureClearInitialState(view);
591                 states = new Object[]{
592                             internalBuildTreeStructureToSave(view),
593                             view.processSaveState(context)};
594             }
595             else
596             {
597                 states = new HashMap<String, Object>();
598 
599                 Object faceletViewState = view.getAttributes().get(ComponentSupport.FACELET_STATE_INSTANCE);
600                 if (faceletViewState != null)
601                 {
602                     ((Map<String, Object>)states).put(ComponentSupport.FACELET_STATE_INSTANCE,
603                             UIComponentBase.saveAttachedState(context, faceletViewState));
604                     //Do not save on UIViewRoot
605                     view.getAttributes().remove(ComponentSupport.FACELET_STATE_INSTANCE);
606                 }
607                 if (isSaveStateWithVisitTreeOnPSS(context))
608                 {
609                     saveStateOnMapVisitTree(context,(Map<String,Object>) states, view);
610                 }
611                 else
612                 {
613                     saveStateOnMap(context,(Map<String,Object>) states, view);
614                 }
615                 
616                 if ( ((Map<String,Object>)states).isEmpty())
617                 {
618                     states = null;
619                 }
620             }
621             
622             // TODO: not sure the best way to handle dynamic adds/removes as mandated by the spec.
623             
624             // As required by ResponseStateManager, the return value is an Object array.  First
625             // element is the structure object, second is the state map.
626             Long uniqueIdCount = (Long) view.getAttributes().get(UNIQUE_ID_COUNTER_KEY);
627             if (uniqueIdCount != null && !uniqueIdCount.equals(1L))
628             {
629                 serializedView = new Object[] { null, states, uniqueIdCount };
630             }
631             else if (states == null)
632             {
633                 serializedView = EMPTY_STATES;
634             }
635             else
636             {
637                 serializedView = new Object[] { null, states };
638             }
639             
640             //externalContext.getRequestMap().put(SERIALIZED_VIEW_REQUEST_ATTR,
641             //        getStateCache().encodeSerializedState(context, serializedView));
642 
643             context.getAttributes().put(SERIALIZED_VIEW_REQUEST_ATTR, serializedView);
644 
645         }
646         
647         //if (!context.getApplication().getStateManager().isSavingStateInClient(context))
648         //{
649         //getStateCache().saveSerializedView(context, serializedView);
650         //}
651         
652         return serializedView;
653     }
654     
655     private void restoreViewRootOnlyFromMap(
656             final FacesContext context, final Object viewState,
657             final UIComponent view)
658     {
659         // Only viewState found, process it but skip tree
660         // traversal, saving some time.
661         try
662         {
663             //Restore view
664             view.pushComponentToEL(context, view);
665             if (viewState != null)
666             {
667                 if (!(viewState instanceof AttachedFullStateWrapper))
668                 {
669                     try
670                     {
671                         view.restoreState(context, viewState);
672                     }
673                     catch(Exception e)
674                     {
675                         throw new IllegalStateException(
676                                 "Error restoring component: "+
677                                 view.getClientId(context), e);
678                     }
679                 }
680             }
681         }
682         finally
683         {
684              view.popComponentFromEL(context);
685         }
686     }
687     
688     private void restoreStateFromMap(final FacesContext context, final Map<String,Object> states,
689             final UIComponent component)
690     {
691         if (states == null)
692         {
693             return;
694         }
695         
696         try
697         {
698             //Restore view
699             component.pushComponentToEL(context, component);
700             Object state = states.get(component.getClientId(context));
701             if (state != null)
702             {
703                 if (state instanceof AttachedFullStateWrapper)
704                 {
705                     //Don't restore this one! It will be restored when the algorithm remove and add it.
706                     return;
707                 }
708                 try
709                 {
710                     component.restoreState(context, state);
711                 }
712                 catch(Exception e)
713                 {
714                     throw new IllegalStateException("Error restoring component: "+component.getClientId(context), e);
715                 }
716             }
717     
718             //Scan children
719             if (component.getChildCount() > 0)
720             {
721                 //String currentClientId = component.getClientId();
722                 
723                 List<UIComponent> children  = component.getChildren();
724                 for (int i = 0; i < children.size(); i++)
725                 {
726                     UIComponent child = children.get(i);
727                     if (child != null && !child.isTransient())
728                     {
729                         restoreStateFromMap( context, states, child);
730                     }
731                 }
732             }
733     
734             //Scan facets
735             if (component.getFacetCount() > 0)
736             {
737                 Map<String, UIComponent> facetMap = component.getFacets();
738                 
739                 for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
740                 {
741                     UIComponent child = entry.getValue();
742                     if (child != null && !child.isTransient())
743                     {
744                         //String facetName = entry.getKey();
745                         restoreStateFromMap( context, states, child);
746                     }
747                 }
748             }
749         }
750         finally
751         {
752             component.popComponentFromEL(context);
753         }
754     }
755 
756     static List<String> getClientIdsAdded(UIViewRoot root)
757     {
758         return (List<String>) root.getAttributes().get(CLIENTIDS_ADDED);
759     }
760     
761     static void setClientsIdsAdded(UIViewRoot root, List<String> clientIdsList)
762     {
763         root.getAttributes().put(CLIENTIDS_ADDED, clientIdsList);
764     }
765     
766     static List<String> getClientIdsRemoved(UIViewRoot root)
767     {
768         return (List<String>) root.getAttributes().get(CLIENTIDS_REMOVED);
769     }
770     
771     static void setClientsIdsRemoved(UIViewRoot root, List<String> clientIdsList)
772     {
773         root.getAttributes().put(CLIENTIDS_REMOVED, clientIdsList);
774     }
775     
776     @SuppressWarnings("unchecked")
777     private void registerOnAddRemoveList(FacesContext facesContext, String clientId)
778     {
779         UIViewRoot uiViewRoot = facesContext.getViewRoot();
780 
781         List<String> clientIdsAdded = (List<String>) getClientIdsAdded(uiViewRoot);
782         if (clientIdsAdded == null)
783         {
784             //Create a set that preserve insertion order
785             clientIdsAdded = new ArrayList<String>();
786         }
787         clientIdsAdded.add(clientId);
788 
789         setClientsIdsAdded(uiViewRoot, clientIdsAdded);
790 
791         List<String> clientIdsRemoved = (List<String>) getClientIdsRemoved(uiViewRoot);
792         if (clientIdsRemoved == null)
793         {
794             //Create a set that preserve insertion order
795             clientIdsRemoved = new ArrayList<String>();
796         }
797 
798         clientIdsRemoved.add(clientId);
799 
800         setClientsIdsRemoved(uiViewRoot, clientIdsRemoved);
801     }
802     
803     @SuppressWarnings("unchecked")
804     private void registerOnAddList(FacesContext facesContext, String clientId)
805     {
806         UIViewRoot uiViewRoot = facesContext.getViewRoot();
807 
808         List<String> clientIdsAdded = (List<String>) getClientIdsAdded(uiViewRoot);
809         if (clientIdsAdded == null)
810         {
811             //Create a set that preserve insertion order
812             clientIdsAdded = new ArrayList<String>();
813         }
814         clientIdsAdded.add(clientId);
815 
816         setClientsIdsAdded(uiViewRoot, clientIdsAdded);
817     }
818 
819     public boolean isSaveStateWithVisitTreeOnPSS(FacesContext facesContext)
820     {
821         if (_saveStateWithVisitTreeOnPSS == null)
822         {
823             _saveStateWithVisitTreeOnPSS
824                     = WebConfigParamUtils.getBooleanInitParameter(facesContext.getExternalContext(),
825                     SAVE_STATE_WITH_VISIT_TREE_ON_PSS, Boolean.TRUE);
826         }
827         return Boolean.TRUE.equals(_saveStateWithVisitTreeOnPSS);
828     }
829 
830     private void saveStateOnMapVisitTree(final FacesContext facesContext, final Map<String,Object> states,
831             final UIViewRoot uiViewRoot)
832     {
833         facesContext.getAttributes().put(SKIP_ITERATION_HINT, Boolean.TRUE);
834         try
835         {
836             uiViewRoot.visitTree( getVisitContextFactory().getVisitContext(
837                     facesContext, null, null), new VisitCallback()
838             {
839                 public VisitResult visit(VisitContext context, UIComponent target)
840                 {
841                     FacesContext facesContext = context.getFacesContext();
842                     Object state;
843                     
844                     if ((target == null) || target.isTransient())
845                     {
846                         // No need to bother with these components or their children.
847                         
848                         return VisitResult.REJECT;
849                     }
850                     
851                     ComponentState componentAddedAfterBuildView
852                             = (ComponentState) target.getAttributes().get(COMPONENT_ADDED_AFTER_BUILD_VIEW);
853                     
854                     //Note if UIViewRoot has this marker, JSF 1.2 like state saving is used.
855                     if (componentAddedAfterBuildView != null && (target.getParent() != null))
856                     {
857                         if (ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
858                         {
859                             registerOnAddRemoveList(facesContext, target.getClientId(facesContext));
860                             target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
861                         }
862                         else if (ComponentState.ADD.equals(componentAddedAfterBuildView))
863                         {
864                             registerOnAddList(facesContext, target.getClientId(facesContext));
865                             target.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
866                         }
867                         else if (ComponentState.ADDED.equals(componentAddedAfterBuildView))
868                         {
869                             registerOnAddList(facesContext, target.getClientId(facesContext));
870                         }
871                         ensureClearInitialState(target);
872                         //Save all required info to restore the subtree.
873                         //This includes position, structure and state of subtree
874                         
875                         int childIndex = target.getParent().getChildren().indexOf(target);
876                         if (childIndex >= 0)
877                         {
878                             states.put(target.getClientId(facesContext), new AttachedFullStateWrapper( 
879                                     new Object[]{
880                                         target.getParent().getClientId(facesContext),
881                                         null,
882                                         childIndex,
883                                         internalBuildTreeStructureToSave(target),
884                                         target.processSaveState(facesContext)}));
885                         }
886                         else
887                         {
888                             String facetName = null;
889                             if (target.getParent().getFacetCount() > 0)
890                             {
891                                 for (Map.Entry<String, UIComponent> entry : target.getParent().getFacets().entrySet()) 
892                                 {
893                                     if (target.equals(entry.getValue()))
894                                     {
895                                         facetName = entry.getKey();
896                                         break;
897                                     }
898                                 }
899                             }
900                             states.put(target.getClientId(facesContext),new AttachedFullStateWrapper(new Object[]{
901                                     target.getParent().getClientId(facesContext),
902                                     facetName,
903                                     null,
904                                     internalBuildTreeStructureToSave(target),
905                                     target.processSaveState(facesContext)}));
906                         }
907                         return VisitResult.REJECT;
908                     }
909                     else if (target.getParent() != null)
910                     {
911                         state = target.saveState (facesContext);
912                         
913                         if (state != null)
914                         {
915                             // Save by client ID into our map.
916                             
917                             states.put (target.getClientId (facesContext), state);
918                         }
919                         
920                         return VisitResult.ACCEPT;
921                     }
922                     else
923                     {
924                         //Only UIViewRoot has no parent in a component tree.
925                         return VisitResult.ACCEPT;
926                     }
927                 }
928             });
929         }
930         finally
931         {
932             facesContext.getAttributes().remove(SKIP_ITERATION_HINT);
933         }
934         
935         Object state = uiViewRoot.saveState (facesContext);
936         if (state != null)
937         {
938             // Save by client ID into our map.
939             
940             states.put (uiViewRoot.getClientId (facesContext), state);
941         }
942     }
943 
944     private void saveStateOnMap(final FacesContext context, final Map<String,Object> states,
945             final UIComponent component)
946     {
947         ComponentState componentAddedAfterBuildView = null;
948         try
949         {
950             component.pushComponentToEL(context, component);
951             
952             //Scan children
953             if (component.getChildCount() > 0)
954             {
955                 List<UIComponent> children  = component.getChildren();
956                 for (int i = 0; i < children.size(); i++)
957                 {
958                     UIComponent child = children.get(i);
959                     if (child != null && !child.isTransient())
960                     {
961                         componentAddedAfterBuildView
962                                 = (ComponentState) child.getAttributes().get(COMPONENT_ADDED_AFTER_BUILD_VIEW);
963                         if (componentAddedAfterBuildView != null)
964                         {
965                             if (ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
966                             {
967                                 registerOnAddRemoveList(context, child.getClientId(context));
968                                 child.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
969                             }
970                             else if (ComponentState.ADD.equals(componentAddedAfterBuildView))
971                             {
972                                 registerOnAddList(context, child.getClientId(context));
973                                 child.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
974                             }
975                             else if (ComponentState.ADDED.equals(componentAddedAfterBuildView))
976                             {
977                                 registerOnAddList(context, child.getClientId(context));
978                             }
979                             ensureClearInitialState(child);
980                             //Save all required info to restore the subtree.
981                             //This includes position, structure and state of subtree
982                             states.put(child.getClientId(context), new AttachedFullStateWrapper( 
983                                     new Object[]{
984                                         component.getClientId(context),
985                                         null,
986                                         i,
987                                         internalBuildTreeStructureToSave(child),
988                                         child.processSaveState(context)}));
989                         }
990                         else
991                         {
992                             saveStateOnMap( context, states, child);
993                         }
994                     }
995                 }
996             }
997     
998             //Scan facets
999             
1000             if (component.getFacetCount() > 0)
1001             {
1002                 Map<String, UIComponent> facetMap = component.getFacets();
1003                 
1004                 for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
1005                 {
1006                     UIComponent child = entry.getValue();
1007                     if (child != null && !child.isTransient())
1008                     {
1009                         String facetName = entry.getKey();
1010                         componentAddedAfterBuildView
1011                                 = (ComponentState) child.getAttributes().get(COMPONENT_ADDED_AFTER_BUILD_VIEW);
1012                         if (componentAddedAfterBuildView != null)
1013                         {
1014                             if (ComponentState.REMOVE_ADD.equals(componentAddedAfterBuildView))
1015                             {
1016                                 registerOnAddRemoveList(context, child.getClientId(context));
1017                                 child.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1018                             }
1019                             else if (ComponentState.ADD.equals(componentAddedAfterBuildView))
1020                             {
1021                                 registerOnAddList(context, child.getClientId(context));
1022                                 child.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADDED);
1023                             }
1024                             else if (ComponentState.ADDED.equals(componentAddedAfterBuildView))
1025                             {
1026                                 registerOnAddList(context, child.getClientId(context));
1027                             }
1028                             //Save all required info to restore the subtree.
1029                             //This includes position, structure and state of subtree
1030                             ensureClearInitialState(child);
1031                             states.put(child.getClientId(context),new AttachedFullStateWrapper(new Object[]{
1032                                 component.getClientId(context),
1033                                 facetName,
1034                                 null,
1035                                 internalBuildTreeStructureToSave(child),
1036                                 child.processSaveState(context)}));
1037                         }
1038                         else
1039                         {
1040                             saveStateOnMap( context, states, child);
1041                         }
1042                     }
1043                 }
1044             }
1045             
1046             //Save state        
1047             Object savedState = component.saveState(context);
1048             
1049             //Only save if the value returned is null
1050             if (savedState != null)
1051             {
1052                 states.put(component.getClientId(context), savedState);            
1053             }
1054         }
1055         finally
1056         {
1057             component.popComponentFromEL(context);
1058         }
1059     }
1060     
1061     protected void ensureClearInitialState(UIComponent c)
1062     {
1063         c.clearInitialState();
1064         if (c.getChildCount() > 0)
1065         {
1066             for (int i = 0, childCount = c.getChildCount(); i < childCount; i++)
1067             {
1068                 UIComponent child = c.getChildren().get(i);
1069                 ensureClearInitialState(child);
1070             }
1071         }
1072         if (c.getFacetCount() > 0)
1073         {
1074             for (UIComponent child : c.getFacets().values())
1075             {
1076                 ensureClearInitialState(child);
1077             }
1078         }
1079     }
1080     
1081     public void suscribeListeners(UIViewRoot uiViewRoot)
1082     {
1083         PostAddPreRemoveFromViewListener componentListener = new PostAddPreRemoveFromViewListener();
1084         uiViewRoot.subscribeToViewEvent(PostAddToViewEvent.class, componentListener);
1085         uiViewRoot.subscribeToViewEvent(PreRemoveFromViewEvent.class, componentListener);
1086     }
1087     
1088     private void checkIds (FacesContext context, UIComponent component, Set<String> existingIds)
1089     {
1090         String id;
1091         Iterator<UIComponent> children;
1092         
1093         if (component == null)
1094         {
1095             return;
1096         }
1097         
1098         // Need to use this form of the client ID method so we generate the client-side ID.
1099         
1100         id = component.getClientId (context);
1101         
1102         if (existingIds.contains (id))
1103         {
1104             throw new IllegalStateException ("component with duplicate id \"" + id + "\" found");
1105         }
1106         
1107         existingIds.add (id);
1108         
1109         int facetCount = component.getFacetCount();
1110         if (facetCount > 0)
1111         {
1112             for (UIComponent facet : component.getFacets().values())
1113             {
1114                 checkIds (context, facet, existingIds);
1115             }
1116         }
1117         for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
1118         {
1119             UIComponent child = component.getChildren().get(i);
1120             checkIds (context, child, existingIds);
1121         }
1122     }
1123     
1124     protected RenderKitFactory getRenderKitFactory()
1125     {
1126         if (_renderKitFactory == null)
1127         {
1128             _renderKitFactory = (RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY);
1129         }
1130         return _renderKitFactory;
1131     }
1132     
1133     protected VisitContextFactory getVisitContextFactory()
1134     {
1135         if (_visitContextFactory == null)
1136         {
1137             _visitContextFactory = (VisitContextFactory)FactoryFinder.getFactory(FactoryFinder.VISIT_CONTEXT_FACTORY);
1138         }
1139         return _visitContextFactory;
1140     }
1141 
1142     protected String getCheckIdProductionMode(FacesContext facesContext)
1143     {
1144         if (_checkIdsProductionMode == null)
1145         {
1146             _checkIdsProductionMode
1147                     = WebConfigParamUtils.getStringInitParameter(facesContext.getExternalContext(),
1148                     CHECK_ID_PRODUCTION_MODE, CHECK_ID_PRODUCTION_MODE_DEFAULT); //default (auto)
1149         }
1150         return _checkIdsProductionMode;
1151     }
1152 
1153     
1154     public static class PostAddPreRemoveFromViewListener implements SystemEventListener
1155     {
1156         private transient FacesContext _facesContext;
1157         
1158         private transient Boolean _isRefreshOnTransientBuildPreserveState;
1159 
1160         public boolean isListenerForSource(Object source)
1161         {
1162             // PostAddToViewEvent and PreRemoveFromViewEvent are
1163             // called from UIComponentBase.setParent
1164             return (source instanceof UIComponent);
1165         }
1166         
1167         private boolean isRefreshOnTransientBuildPreserveState()
1168         {
1169             if (_isRefreshOnTransientBuildPreserveState == null)
1170             {
1171                 _isRefreshOnTransientBuildPreserveState = MyfacesConfig.getCurrentInstance(
1172                         _facesContext.getExternalContext()).isRefreshTransientBuildOnPSSPreserveState();
1173             }
1174             return _isRefreshOnTransientBuildPreserveState;
1175         }
1176 
1177         public void processEvent(SystemEvent event)
1178         {
1179             UIComponent component = (UIComponent) event.getSource();
1180             
1181             if (component.isTransient())
1182             {
1183                 return;
1184             }
1185             
1186             // This is a view listener. It is not saved on the state and this listener
1187             // is suscribed each time the view is restored, so we can cache facesContext
1188             // here
1189             if (_facesContext == null)
1190             {
1191                 _facesContext = FacesContext.getCurrentInstance();
1192             }
1193             //FacesContext facesContext = FacesContext.getCurrentInstance();
1194             //if (FaceletViewDeclarationLanguage.isRefreshingTransientBuild(facesContext))
1195             //{
1196             //    return;
1197             //}
1198             
1199             if (event instanceof PostAddToViewEvent)
1200             {
1201                 if (!isRefreshOnTransientBuildPreserveState() &&
1202                     Boolean.TRUE.equals(_facesContext.getAttributes().get("javax.faces.IS_BUILDING_INITIAL_STATE")))
1203                 {
1204                     return;
1205                 }
1206 
1207                 //PostAddToViewEvent
1208                 component.getAttributes().put(COMPONENT_ADDED_AFTER_BUILD_VIEW, ComponentState.ADD);
1209             }
1210             else
1211             {
1212                 //FacesContext facesContext = FacesContext.getCurrentInstance();
1213                 // In this case if we are removing components on build, it is not necessary to register
1214                 // again the current id, and its more, it could cause a concurrent exception. But note
1215                 // we need to propagate PreRemoveFromViewEvent, otherwise the view will not be restored
1216                 // correctly.
1217                 if (FaceletViewDeclarationLanguage.isRemovingComponentBuild(_facesContext))
1218                 {
1219                     return;
1220                 }
1221 
1222                 if (!isRefreshOnTransientBuildPreserveState() &&
1223                     FaceletCompositionContext.getCurrentInstance(_facesContext) != null &&
1224                     (component.getAttributes().containsKey(ComponentSupport.MARK_CREATED) ||
1225                      component.getAttributes().containsKey(ComponentSupport.COMPONENT_ADDED_BY_HANDLER_MARKER))
1226                     )
1227                 {
1228                     // Components removed by facelets algorithm does not need to be registered
1229                     // unless preserve state mode is used, because PSS initial state is changed
1230                     // to restore delta properly.
1231                     // MYFACES-3554 It is possible to find use cases where a component
1232                     // created by a facelet tag is changed dynamically in some way in render
1233                     // response time, so we need to check here also when facelets algorithm
1234                     // is running or not. 
1235                     return;
1236                 }
1237                 
1238                 //PreRemoveFromViewEvent
1239                 UIViewRoot uiViewRoot = _facesContext.getViewRoot();
1240                 
1241                 List<String> clientIdsRemoved = getClientIdsRemoved(uiViewRoot);
1242                 if (clientIdsRemoved == null)
1243                 {
1244                     //Create a set that preserve insertion order
1245                     clientIdsRemoved = new ArrayList<String>();
1246                 }
1247                 clientIdsRemoved.add(component.getClientId(_facesContext));
1248                 setClientsIdsRemoved(uiViewRoot, clientIdsRemoved);
1249             }
1250         }
1251     }
1252     
1253     private static TreeStructComponent internalBuildTreeStructureToSave(UIComponent component)
1254     {
1255         TreeStructComponent structComp = new TreeStructComponent(component.getClass().getName(),
1256                                                                  component.getId());
1257 
1258         //children
1259         if (component.getChildCount() > 0)
1260         {
1261             List<TreeStructComponent> structChildList = new ArrayList<TreeStructComponent>();
1262             for (int i = 0, childCount = component.getChildCount(); i < childCount; i++)
1263             {
1264                 UIComponent child = component.getChildren().get(i);     
1265                 if (!child.isTransient())
1266                 {
1267                     TreeStructComponent structChild = internalBuildTreeStructureToSave(child);
1268                     structChildList.add(structChild);
1269                 }
1270             }
1271             
1272             TreeStructComponent[] childArray = structChildList.toArray(new TreeStructComponent[structChildList.size()]);
1273             structComp.setChildren(childArray);
1274         }
1275 
1276         //facets
1277         
1278         if (component.getFacetCount() > 0)
1279         {
1280             Map<String, UIComponent> facetMap = component.getFacets();
1281             List<Object[]> structFacetList = new ArrayList<Object[]>();
1282             for (Map.Entry<String, UIComponent> entry : facetMap.entrySet())
1283             {
1284                 UIComponent child = entry.getValue();
1285                 if (!child.isTransient())
1286                 {
1287                     String facetName = entry.getKey();
1288                     TreeStructComponent structChild = internalBuildTreeStructureToSave(child);
1289                     structFacetList.add(new Object[] {facetName, structChild});
1290                 }
1291             }
1292             
1293             Object[] facetArray = structFacetList.toArray(new Object[structFacetList.size()]);
1294             structComp.setFacets(facetArray);
1295         }
1296 
1297         return structComp;
1298     }
1299     
1300     private static UIComponent internalRestoreTreeStructure(TreeStructComponent treeStructComp)
1301     {
1302         String compClass = treeStructComp.getComponentClass();
1303         String compId = treeStructComp.getComponentId();
1304         UIComponent component = (UIComponent)ClassUtils.newInstance(compClass);
1305         component.setId(compId);
1306 
1307         //children
1308         TreeStructComponent[] childArray = treeStructComp.getChildren();
1309         if (childArray != null)
1310         {
1311             List<UIComponent> childList = component.getChildren();
1312             for (int i = 0, len = childArray.length; i < len; i++)
1313             {
1314                 UIComponent child = internalRestoreTreeStructure(childArray[i]);
1315                 childList.add(child);
1316             }
1317         }
1318 
1319         //facets
1320         Object[] facetArray = treeStructComp.getFacets();
1321         if (facetArray != null)
1322         {
1323             Map<String, UIComponent> facetMap = component.getFacets();
1324             for (int i = 0, len = facetArray.length; i < len; i++)
1325             {
1326                 Object[] tuple = (Object[])facetArray[i];
1327                 String facetName = (String)tuple[0];
1328                 TreeStructComponent structChild = (TreeStructComponent)tuple[1];
1329                 UIComponent child = internalRestoreTreeStructure(structChild);
1330                 facetMap.put(facetName, child);
1331             }
1332         }
1333 
1334         return component;
1335     }
1336 
1337     public static class TreeStructComponent implements Serializable
1338     {
1339         private static final long serialVersionUID = 5069109074684737231L;
1340         private String _componentClass;
1341         private String _componentId;
1342         private TreeStructComponent[] _children = null; // Array of children
1343         private Object[] _facets = null; // Array of Array-tuples with Facetname and TreeStructComponent
1344 
1345         TreeStructComponent(String componentClass, String componentId)
1346         {
1347             _componentClass = componentClass;
1348             _componentId = componentId;
1349         }
1350 
1351         public String getComponentClass()
1352         {
1353             return _componentClass;
1354         }
1355 
1356         public String getComponentId()
1357         {
1358             return _componentId;
1359         }
1360 
1361         void setChildren(TreeStructComponent[] children)
1362         {
1363             _children = children;
1364         }
1365 
1366         TreeStructComponent[] getChildren()
1367         {
1368             return _children;
1369         }
1370 
1371         Object[] getFacets()
1372         {
1373             return _facets;
1374         }
1375 
1376         void setFacets(Object[] facets)
1377         {
1378             _facets = facets;
1379         }
1380     }
1381     
1382 }