View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package javax.faces.component.behavior;
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.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  
29  import javax.el.ValueExpression;
30  import javax.faces.component.StateHelper;
31  import javax.faces.component.StateHolder;
32  import javax.faces.component.UIComponentBase;
33  import javax.faces.context.FacesContext;
34  
35  /**
36   * A delta enabled state holder implementing the StateHolder Interface. 
37   * <p>
38   * Components implementing the PartalStateHolder interface have an initial state
39   * and delta states, the initial state is the one holding all root values
40   * and deltas store differences to the initial states
41   * </p>
42   * <p>
43   * For components not implementing partial state saving only the initial states are
44   * of importance, everything is stored and restored continously there
45   * </p> 
46   * <p>
47   * The state helper seems to have three internal storage mechanisms:
48   * one being a list which stores plain values,
49   * one being a key value pair which stores key values in maps
50   * add serves the plain list type while put serves the 
51   * key value type, 
52   * the third is the value which has to be stored plainly as is!
53   * </p>
54   * In other words, this map can be seen as a composite map. It has two maps: 
55   * initial state map and delta map.
56   * <p> 
57   * If delta map is used (method component.initialStateMarked() ), 
58   * base or initial state map cannot be changed, since all changes 
59   * should be tracked on delta map.
60   * </p>
61   * <p> 
62   * The intention of this class is just hold property values
63   * and do a clean separation between initial state and delta.
64   * </p>
65   * <p>
66   * The code from this class comes from a refactor of 
67   * org.apache.myfaces.trinidad.bean.util.PropertyHashMap
68   * </p>
69   * <p>
70   * The context from this class comes and that should be taken into account
71   * is this:
72   * </p>
73   * <p> 
74   * First request:
75   * </p>
76   * <ul>
77   *   <li> A new template is created (using 
78   *   javax.faces.view.ViewDeclarationLanguage.buildView method)
79   *   and component.markInitialState is called from its related TagHandler classes 
80   *  (see javax.faces.view.facelets.ComponentHandler ).
81   *   When this method is executed, the component tree was populated from the values
82   *   set in the facelet abstract syntax tree (or in other words composition of 
83   *   facelets templates). </li>
84   *   <li> From this point all updates on the variables are considered "delta". </li>
85   *   <li> SaveState, if initialStateMarked is true, only delta is saved. </li>
86   * </ul>
87   * <p>
88   * Second request (and next ones)
89   * </p>
90   * <ul>
91   *   <li> A new template is created and component.markInitialState is called from
92   *   its related TagHandler classes again. In this way, components like c:forEach 
93   *   or c:if, that add or remove components could notify about this and handle 
94   *   them properly (see javax.faces.view.StateManagementStrategy). Note that a 
95   *   component restored using this method is no different as the same component 
96   *   at the first request at the same time. </li>
97   *   <li> A call for restoreState is done, passing the delta as object value. If no 
98   *   delta, the state is complete and no call is triggered. </li>
99   *   <li> Lifecycle occur, changing the necessary stuff. </li>
100  *   <li> SaveState, if initialStateMarked is true, only delta is saved. </li>
101  * </ul>
102  * <p>
103  * From the previous analysis, the following conclusions arise:
104  * <ul>
105  *   <li>This class only needs to keep track of delta changes, so when 
106  *   restoreState/saveState is called, the right objects are passed.</li>
107  *   <li>UIComponent.clearInitialState is used to reset the partial
108  *   state holder to a non delta state, so the state to be saved by
109  *   saveState is no longer a delta instead is a full state. If a call
110  *   to clearInitialState occur it is not expected a call for 
111  *   UIComponent.markInitialState occur on the current request.</li>
112  *   <li>The state is handled in the same way on UIData, so components
113  *   inside UIData share its state on all rows. There is no way to save 
114  *   delta per row.</li>
115  *   <li>The map backed by method put(Serializable,String,Object) is
116  *   a replacement of UIComponentBase.attributesMap and UIComponent.bindings map.
117  *   Note that on jsf 1.2, instances saved on attributesMap should not be
118  *   StateHolder, but on jsf 2.0 it is possible to have it. PartialStateHolder
119  *   instances are not handled in this map, or in other words delta state is not
120  *   handled in this classes (markInitialState and clearInitialState is not propagated).</li>
121  *   <li>The list backed by method add(Serializable,Object) should be (is not) a 
122  *   replacement of UIComponentBase.facesListeners, but note that StateHelper
123  *   does not implement PartialStateHolder, and facesListener could have instances
124  *   of that class that needs to be notified when UIComponent.markInitialState or
125  *   UIComponent.clearInitialState is called, or in other words facesListeners
126  *   should deal with PartialStateHolder instances.</li>
127  *   <li>The list backed by method add(Serializable,Object) is 
128  *   a replacement of UIViewRoot.phaseListeners list. Note that instances of
129  *   PhaseListener are not expected to implement StateHolder or PartialStateHolder.</li>
130  * </ul>
131  * </p>
132  * <p>
133  * NOTE: The current implementation of StateHelper on RI does not handle
134  * stateHolder values internally. To prevent problems when developers create
135  * custom components we should do this too. But anyway, the code that 
136  * handle this case should be let here as comment, if some day this feature
137  * is provided. Note than stateHolder aware properties like converter,
138  * validator or listeners should deal with StateHolder or PartialStateHolder
139  * on component classes. 
140  * 
141  * </p>
142  *
143  * @author Werner Punz
144  * @author Leonardo Uribe (latest modification by $Author: bommel $)
145  * @version $Rev: 1187700 $ $Date: 2011-10-22 07:19:37 -0500 (Sat, 22 Oct 2011) $
146  */
147 class _DeltaStateHelper <A extends AjaxBehavior> implements StateHelper
148 {
149 
150     /**
151      * We need to hold a component instance because:
152      * 
153      * - The component is the one who knows if we are on initial or delta mode
154      * - eval assume calls to component.ValueExpression
155      */
156     private A _target;
157 
158     /**
159      * This map holds the full current state
160      */
161     private Map<Serializable, Object> _fullState;
162 
163     /**
164      * This map only keep track of delta changes to be saved
165      */
166     private Map<Serializable, Object> _deltas;
167     
168     /**
169      * This map keep track of StateHolder keys, to be saved when
170      * saveState is called. 
171      */
172     //private Set<Serializable> _stateHolderKeys;  
173 
174     private boolean _transient = false;
175 
176     public _DeltaStateHelper(A target)
177     {
178         super();
179         this._target = target;
180         _fullState = new HashMap<Serializable, Object>();
181         _deltas = null;
182         //_stateHolderKeys = new HashSet<Serializable>();
183     }
184 
185     /**
186      * Used to create delta map on demand
187      * 
188      * @return
189      */
190     private boolean _createDeltas()
191     {
192         if (isInitialStateMarked())
193         {
194             if (_deltas == null)
195             {
196                 _deltas = new HashMap<Serializable, Object>(2);
197             }
198             return true;
199         }
200 
201         return false;
202     }
203     
204     protected boolean isInitialStateMarked()
205     {
206         return _target.initialStateMarked();
207     }
208 
209     public void add(Serializable key, Object value)
210     {
211         if (_createDeltas())
212         {
213             //Track delta case
214             Map<Object, Boolean> deltaListMapValues = (Map<Object, Boolean>) _deltas
215                     .get(key);
216             if (deltaListMapValues == null)
217             {
218                 deltaListMapValues = new InternalDeltaListMap<Object, Boolean>(
219                         3);
220                 _deltas.put(key, deltaListMapValues);
221             }
222             deltaListMapValues.put(value, Boolean.TRUE);
223         }
224 
225         //Handle change on full map
226         List<Object> fullListValues = (List<Object>) _fullState.get(key);
227         if (fullListValues == null)
228         {
229             fullListValues = new InternalList<Object>(3);
230             _fullState.put(key, fullListValues);
231         }
232         fullListValues.add(value);
233     }
234 
235     public Object eval(Serializable key)
236     {
237         Object returnValue = _fullState.get(key);
238         if (returnValue != null)
239         {
240             return returnValue;
241         }
242         ValueExpression expression = _target.getValueExpression(key
243                 .toString());
244         if (expression != null)
245         {
246             return expression.getValue(FacesContext.getCurrentInstance()
247                     .getELContext());
248         }
249         return null;
250     }
251 
252     public Object eval(Serializable key, Object defaultValue)
253     {
254         Object returnValue = _fullState.get(key);
255         if (returnValue != null)
256         {
257             return returnValue;
258         }
259         ValueExpression expression = _target.getValueExpression(key
260                 .toString());
261         if (expression != null)
262         {
263             return expression.getValue(FacesContext.getCurrentInstance()
264                     .getELContext());
265         }
266         return defaultValue;
267     }
268 
269     public Object get(Serializable key)
270     {
271         return _fullState.get(key);
272     }
273 
274     public Object put(Serializable key, Object value)
275     {
276         Object returnValue = null;
277         if (_createDeltas())
278         {
279             if (_deltas.containsKey(key))
280             {
281                 returnValue = _deltas.put(key, value);
282                 _fullState.put(key, value);
283             }
284             else if (value == null && !_fullState.containsKey(key))
285             {
286                 returnValue = null;
287             }
288             else
289             {
290                 _deltas.put(key, value);
291                 returnValue = _fullState.put(key, value);
292             }
293         }
294         else
295         {
296             /*
297             if (value instanceof StateHolder)
298             {
299                 _stateHolderKeys.add(key);
300             }
301             */
302             returnValue = _fullState.put(key, value);
303         }
304         return returnValue;
305     }
306 
307     public Object put(Serializable key, String mapKey, Object value)
308     {
309         boolean returnSet = false;
310         Object returnValue = null;
311         if (_createDeltas())
312         {
313             //Track delta case
314             Map<String, Object> mapValues = (Map<String, Object>) _deltas
315                     .get(key);
316             if (mapValues == null)
317             {
318                 mapValues = new InternalMap<String, Object>();
319                 _deltas.put(key, mapValues);
320             }
321             if (mapValues.containsKey(mapKey))
322             {
323                 returnValue = mapValues.put(mapKey, value);
324                 returnSet = true;
325             }
326             else
327             {
328                 mapValues.put(mapKey, value);
329             }
330         }
331 
332         //Handle change on full map
333         Map<String, Object> mapValues = (Map<String, Object>) _fullState
334                 .get(key);
335         if (mapValues == null)
336         {
337             mapValues = new InternalMap<String, Object>();
338             _fullState.put(key, mapValues);
339         }
340         if (returnSet)
341         {
342             mapValues.put(mapKey, value);
343         }
344         else
345         {
346             returnValue = mapValues.put(mapKey, value);
347         }
348         return returnValue;
349     }
350 
351     public Object remove(Serializable key)
352     {
353         Object returnValue = null;
354         if (_createDeltas())
355         {
356             if (_deltas.containsKey(key))
357             {
358                 // Keep track of the removed values using key/null pair on the delta map
359                 returnValue = _deltas.put(key, null);
360                 _fullState.remove(key);
361             }
362             else
363             {
364                 // Keep track of the removed values using key/null pair on the delta map
365                 _deltas.put(key, null);
366                 returnValue = _fullState.remove(key);
367             }
368         }
369         else
370         {
371             returnValue = _fullState.remove(key);
372         }
373         return returnValue;
374     }
375 
376     public Object remove(Serializable key, Object valueOrKey)
377     {
378         // Comment by lu4242 : The spec javadoc says if it is a Collection 
379         // or Map deal with it. But the intention of this method is work 
380         // with add(?,?) and put(?,?,?), this ones return instances of 
381         // InternalMap and InternalList to prevent mixing, so to be 
382         // consistent we'll cast to those classes here.
383         
384         Object collectionOrMap = _fullState.get(key);
385         Object returnValue = null;
386         if (collectionOrMap instanceof InternalMap)
387         {
388             if (_createDeltas())
389             {
390                 returnValue = _removeValueOrKeyFromMap(_deltas, key,
391                         valueOrKey, true);
392                 _removeValueOrKeyFromMap(_fullState, key, valueOrKey, false);
393             }
394             else
395             {
396                 returnValue = _removeValueOrKeyFromMap(_fullState, key,
397                         valueOrKey, false);
398             }
399         }
400         else if (collectionOrMap instanceof InternalList)
401         {
402             if (_createDeltas())
403             {
404                 returnValue = _removeValueOrKeyFromCollectionDelta(_deltas,
405                         key, valueOrKey);
406                 _removeValueOrKeyFromCollection(_fullState, key, valueOrKey);
407             }
408             else
409             {
410                 returnValue = _removeValueOrKeyFromCollection(_fullState, key,
411                         valueOrKey);
412             }
413         }
414         return returnValue;
415     }
416 
417     private static Object _removeValueOrKeyFromCollectionDelta(
418             Map<Serializable, Object> stateMap, Serializable key,
419             Object valueOrKey)
420     {
421         Object returnValue = null;
422         Map<Object, Boolean> c = (Map<Object, Boolean>) stateMap.get(key);
423         if (c != null)
424         {
425             if (c.containsKey(valueOrKey))
426             {
427                 returnValue = valueOrKey;
428             }
429             c.put(valueOrKey, Boolean.FALSE);
430         }
431         return returnValue;
432     }
433 
434     private static Object _removeValueOrKeyFromCollection(
435             Map<Serializable, Object> stateMap, Serializable key,
436             Object valueOrKey)
437     {
438         Object returnValue = null;
439         Collection c = (Collection) stateMap.get(key);
440         if (c != null)
441         {
442             if (c.remove(valueOrKey))
443             {
444                 returnValue = valueOrKey;
445             }
446             if (c.isEmpty())
447             {
448                 stateMap.remove(key);
449             }
450         }
451         return returnValue;
452     }
453 
454     private static Object _removeValueOrKeyFromMap(
455             Map<Serializable, Object> stateMap, Serializable key,
456             Object valueOrKey, boolean delta)
457     {
458         if (valueOrKey == null)
459         {
460             return null;
461         }
462 
463         Object returnValue = null;
464         Map<String, Object> map = (Map<String, Object>) stateMap.get(key);
465         if (map != null)
466         {
467             if (delta)
468             {
469                 // Keep track of the removed values using key/null pair on the delta map
470                 returnValue = map.put((String) valueOrKey, null);
471             }
472             else
473             {
474                 returnValue = map.remove(valueOrKey);
475             }
476 
477             if (map.isEmpty())
478             {
479                 //stateMap.remove(key);
480                 stateMap.put(key, null);
481             }
482         }
483         return returnValue;
484     }
485 
486     public boolean isTransient()
487     {
488         return _transient;
489     }
490 
491     /**
492      * Serializing cod
493      * the serialized data structure consists of key value pairs unless the value itself is an internal array
494      * or a map in case of an internal array or map the value itself is another array with its initial value
495      * myfaces.InternalArray, myfaces.internalMap
496      *
497      * the internal Array is then mapped to another array
498      *
499      * the internal Map again is then mapped to a map with key value pairs
500      *
501      *
502      */
503     public Object saveState(FacesContext context)
504     {
505         Map serializableMap = (isInitialStateMarked()) ? _deltas : _fullState;
506 
507         if (serializableMap == null || serializableMap.size() == 0)
508         {
509             return null;
510         }
511         
512         /*
513         int stateHolderKeyCount = 0;
514         if (isInitalStateMarked())
515         {
516             for (Iterator<Serializable> it = _stateHolderKeys.iterator(); it.hasNext();)
517             {
518                 Serializable key = it.next();
519                 if (!_deltas.containsKey(key))
520                 {
521                     stateHolderKeyCount++;
522                 }
523             }
524         }*/
525         
526         Map.Entry<Serializable, Object> entry;
527         //entry == key, value, key, value
528         Object[] retArr = new Object[serializableMap.entrySet().size() * 2];
529         //Object[] retArr = new Object[serializableMap.entrySet().size() * 2 + stateHolderKeyCount]; 
530 
531         Iterator<Map.Entry<Serializable, Object>> it = serializableMap
532                 .entrySet().iterator();
533         int cnt = 0;
534         while (it.hasNext())
535         {
536             entry = it.next();
537             retArr[cnt] = entry.getKey();
538 
539             Object value = entry.getValue();
540             
541             // The condition in which the call to saveAttachedState
542             // is to handle List, StateHolder or non Serializable instances.
543             // we check it here, to prevent unnecessary calls.
544             if (value instanceof StateHolder ||
545                 value instanceof List ||
546                 !(value instanceof Serializable))
547             {
548                 Object savedValue = saveAttachedState(context,
549                     value);
550                 retArr[cnt + 1] = savedValue;
551             }
552             else
553             {
554                 retArr[cnt + 1] = value;
555             }
556             cnt += 2;
557         }
558         
559         /*
560         if (isInitalStateMarked())
561         {
562             for (Iterator<Serializable> it2 = _stateHolderKeys.iterator(); it.hasNext();)
563             {
564                 Serializable key = it2.next();
565                 if (!_deltas.containsKey(key))
566                 {
567                     retArr[cnt] = key;
568                     Object value = _fullState.get(key);
569                     if (value instanceof PartialStateHolder)
570                     {
571                         //Could contain delta, save it as _AttachedDeltaState
572                         PartialStateHolder holder = (PartialStateHolder) value;
573                         if (holder.isTransient())
574                         {
575                             retArr[cnt + 1] = null;
576                         }
577                         else
578                         {
579                             retArr[cnt + 1] = new _AttachedDeltaWrapper(value.getClass(), holder.saveState(context));
580                         }
581                     }
582                     else
583                     {
584                         //Save everything
585                         retArr[cnt + 1] = saveAttachedState(context, _fullState.get(key));
586                     }
587                     cnt += 2;
588                 }
589             }
590         }
591         */       
592         return retArr;
593     }
594 
595     public void restoreState(FacesContext context, Object state)
596     {
597         if (state == null)
598             return;
599 
600         Object[] serializedState = (Object[]) state;
601         
602         if (!isInitialStateMarked() && !_fullState.isEmpty())
603         {
604             _fullState.clear();
605             if(_deltas != null)
606             {
607                 _deltas.clear();
608             }
609         }
610 
611         for (int cnt = 0; cnt < serializedState.length; cnt += 2)
612         {
613             Serializable key = (Serializable) serializedState[cnt];
614             Object savedValue = restoreAttachedState(context,
615                     serializedState[cnt + 1]);
616 
617             if (isInitialStateMarked())
618             {
619                 if (savedValue instanceof InternalDeltaListMap)
620                 {
621                     for (Map.Entry<Object, Boolean> mapEntry : ((Map<Object, Boolean>) savedValue)
622                             .entrySet())
623                     {
624                         boolean addOrRemove = mapEntry.getValue();
625                         if (addOrRemove)
626                         {
627                             //add
628                             this.add(key, mapEntry.getKey());
629                         }
630                         else
631                         {
632                             //remove
633                             this.remove(key, mapEntry.getKey());
634                         }
635                     }
636                 }
637                 else if (savedValue instanceof InternalMap)
638                 {
639                     for (Map.Entry<String, Object> mapEntry : ((Map<String, Object>) savedValue)
640                             .entrySet())
641                     {
642                         this.put(key, mapEntry.getKey(), mapEntry.getValue());
643                     }
644                 }
645                 /*
646                 else if (savedValue instanceof _AttachedDeltaWrapper)
647                 {
648                     _AttachedStateWrapper wrapper = (_AttachedStateWrapper) savedValue;
649                     //Restore delta state
650                     ((PartialStateHolder)_fullState.get(key)).restoreState(context, wrapper.getWrappedStateObject());
651                     //Add this key as StateHolder key 
652                     _stateHolderKeys.add(key);
653                 }
654                 */
655                 else
656                 {
657                     put(key, savedValue);
658                 }
659             }
660             else
661             {
662                 put(key, savedValue);
663             }
664         }
665     }
666 
667     public void setTransient(boolean transientValue)
668     {
669         _transient = transientValue;
670     }
671 
672     //We use our own data structures just to make sure
673     //nothing gets mixed up internally
674     static class InternalMap<K, V> extends HashMap<K, V> implements StateHolder
675     {
676         public InternalMap()
677         {
678             super();
679         }
680 
681         public InternalMap(int initialCapacity, float loadFactor)
682         {
683             super(initialCapacity, loadFactor);
684         }
685 
686         public InternalMap(Map<? extends K, ? extends V> m)
687         {
688             super(m);
689         }
690 
691         public InternalMap(int initialSize)
692         {
693             super(initialSize);
694         }
695 
696         public boolean isTransient()
697         {
698             return false;
699         }
700 
701         public void setTransient(boolean newTransientValue)
702         {
703             // No op
704         }
705 
706         public void restoreState(FacesContext context, Object state)
707         {
708             Object[] listAsMap = (Object[]) state;
709             for (int cnt = 0; cnt < listAsMap.length; cnt += 2)
710             {
711                 this.put((K) listAsMap[cnt], (V) UIComponentBase
712                         .restoreAttachedState(context, listAsMap[cnt + 1]));
713             }
714         }
715 
716         public Object saveState(FacesContext context)
717         {
718             int cnt = 0;
719             Object[] mapArr = new Object[this.size() * 2];
720             for (Map.Entry<K, V> entry : this.entrySet())
721             {
722                 mapArr[cnt] = entry.getKey();
723                 Object value = entry.getValue();
724                 
725                 if (value instanceof StateHolder ||
726                     value instanceof List ||
727                     !(value instanceof Serializable))
728                 {
729                     mapArr[cnt + 1] = saveAttachedState(context, value);
730                 }
731                 else
732                 {
733                     mapArr[cnt + 1] = value;
734                 }
735                 cnt += 2;
736             }
737             return mapArr;
738         }
739     }
740 
741     /**
742      * Map used to keep track of list changes 
743      */
744     static class InternalDeltaListMap<K, V> extends InternalMap<K, V>
745     {
746 
747         public InternalDeltaListMap()
748         {
749             super();
750         }
751 
752         public InternalDeltaListMap(int initialCapacity, float loadFactor)
753         {
754             super(initialCapacity, loadFactor);
755         }
756 
757         public InternalDeltaListMap(int initialSize)
758         {
759             super(initialSize);
760         }
761 
762         public InternalDeltaListMap(Map<? extends K, ? extends V> m)
763         {
764             super(m);
765         }
766     }
767 
768     static class InternalList<T> extends ArrayList<T> implements StateHolder
769     {
770         public InternalList()
771         {
772             super();
773         }
774 
775         public InternalList(Collection<? extends T> c)
776         {
777             super(c);
778         }
779 
780         public InternalList(int initialSize)
781         {
782             super(initialSize);
783         }
784 
785         public boolean isTransient()
786         {
787             return false;
788         }
789 
790         public void setTransient(boolean newTransientValue)
791         {
792         }
793 
794         public void restoreState(FacesContext context, Object state)
795         {
796             Object[] listAsArr = (Object[]) state;
797             //since all other options would mean dual iteration 
798             //we have to do it the hard way
799             for (Object elem : listAsArr)
800             {
801                 add((T) restoreAttachedState(context, elem));
802             }
803         }
804 
805         public Object saveState(FacesContext context)
806         {
807             Object[] values = new Object[size()];
808             for (int i = 0; i < size(); i++)
809             {
810                 Object value = get(i);
811                 
812                 if (value instanceof StateHolder ||
813                     value instanceof List ||
814                     !(value instanceof Serializable))
815                 {
816                     values[i] = saveAttachedState(context, value);
817                 }
818                 else
819                 {
820                     values[i] = value;
821                 }                
822             }
823             return values;
824         }
825     }
826     
827     private static Object saveAttachedState(FacesContext context, Object attachedObject)
828     {
829         if (context == null)
830         {
831             throw new NullPointerException ("context");
832         }
833         
834         if (attachedObject == null)
835             return null;
836         // StateHolder interface should take precedence over
837         // List children
838         if (attachedObject instanceof StateHolder)
839         {
840             StateHolder holder = (StateHolder) attachedObject;
841             if (holder.isTransient())
842             {
843                 return null;
844             }
845 
846             return new _AttachedStateWrapper(attachedObject.getClass(), holder.saveState(context));
847         }        
848         else if (attachedObject instanceof List)
849         {
850             List<Object> lst = new ArrayList<Object>(((List<?>) attachedObject).size());
851             for (Object item : (List<?>) attachedObject)
852             {
853                 if (item != null)
854                 {
855                     lst.add(saveAttachedState(context, item));
856                 }
857             }
858 
859             return new _AttachedListStateWrapper(lst);
860         }
861         else if (attachedObject instanceof Serializable)
862         {
863             return attachedObject;
864         }
865         else
866         {
867             return new _AttachedStateWrapper(attachedObject.getClass(), null);
868         }
869     }
870 
871     private static Object restoreAttachedState(FacesContext context, Object stateObj) throws IllegalStateException
872     {
873         if (context == null)
874             throw new NullPointerException("context");
875         if (stateObj == null)
876             return null;
877         if (stateObj instanceof _AttachedListStateWrapper)
878         {
879             List<Object> lst = ((_AttachedListStateWrapper) stateObj).getWrappedStateList();
880             List<Object> restoredList = new ArrayList<Object>(lst.size());
881             for (Object item : lst)
882             {
883                 restoredList.add(restoreAttachedState(context, item));
884             }
885             return restoredList;
886         }
887         else if (stateObj instanceof _AttachedStateWrapper)
888         {
889             Class<?> clazz = ((_AttachedStateWrapper) stateObj).getClazz();
890             Object restoredObject;
891             try
892             {
893                 restoredObject = clazz.newInstance();
894             }
895             catch (InstantiationException e)
896             {
897                 throw new RuntimeException("Could not restore StateHolder of type " + clazz.getName()
898                         + " (missing no-args constructor?)", e);
899             }
900             catch (IllegalAccessException e)
901             {
902                 throw new RuntimeException(e);
903             }
904             if (restoredObject instanceof StateHolder)
905             {
906                 _AttachedStateWrapper wrapper = (_AttachedStateWrapper) stateObj;
907                 Object wrappedState = wrapper.getWrappedStateObject();
908 
909                 StateHolder holder = (StateHolder) restoredObject;
910                 holder.restoreState(context, wrappedState);
911             }
912             return restoredObject;
913         }
914         else
915         {
916             return stateObj;
917         }
918     }
919 }