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: struberg $)
145 * @version $Rev: 1188235 $ $Date: 2011-10-24 12:09:33 -0500 (Mon, 24 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 {
599 return;
600 }
601
602 Object[] serializedState = (Object[]) state;
603
604 if (!isInitialStateMarked() && !_fullState.isEmpty())
605 {
606 _fullState.clear();
607 if(_deltas != null)
608 {
609 _deltas.clear();
610 }
611 }
612
613 for (int cnt = 0; cnt < serializedState.length; cnt += 2)
614 {
615 Serializable key = (Serializable) serializedState[cnt];
616 Object savedValue = restoreAttachedState(context,
617 serializedState[cnt + 1]);
618
619 if (isInitialStateMarked())
620 {
621 if (savedValue instanceof InternalDeltaListMap)
622 {
623 for (Map.Entry<Object, Boolean> mapEntry : ((Map<Object, Boolean>) savedValue)
624 .entrySet())
625 {
626 boolean addOrRemove = mapEntry.getValue();
627 if (addOrRemove)
628 {
629 //add
630 this.add(key, mapEntry.getKey());
631 }
632 else
633 {
634 //remove
635 this.remove(key, mapEntry.getKey());
636 }
637 }
638 }
639 else if (savedValue instanceof InternalMap)
640 {
641 for (Map.Entry<String, Object> mapEntry : ((Map<String, Object>) savedValue)
642 .entrySet())
643 {
644 this.put(key, mapEntry.getKey(), mapEntry.getValue());
645 }
646 }
647 /*
648 else if (savedValue instanceof _AttachedDeltaWrapper)
649 {
650 _AttachedStateWrapper wrapper = (_AttachedStateWrapper) savedValue;
651 //Restore delta state
652 ((PartialStateHolder)_fullState.get(key)).restoreState(context, wrapper.getWrappedStateObject());
653 //Add this key as StateHolder key
654 _stateHolderKeys.add(key);
655 }
656 */
657 else
658 {
659 put(key, savedValue);
660 }
661 }
662 else
663 {
664 put(key, savedValue);
665 }
666 }
667 }
668
669 public void setTransient(boolean transientValue)
670 {
671 _transient = transientValue;
672 }
673
674 //We use our own data structures just to make sure
675 //nothing gets mixed up internally
676 static class InternalMap<K, V> extends HashMap<K, V> implements StateHolder
677 {
678 public InternalMap()
679 {
680 super();
681 }
682
683 public InternalMap(int initialCapacity, float loadFactor)
684 {
685 super(initialCapacity, loadFactor);
686 }
687
688 public InternalMap(Map<? extends K, ? extends V> m)
689 {
690 super(m);
691 }
692
693 public InternalMap(int initialSize)
694 {
695 super(initialSize);
696 }
697
698 public boolean isTransient()
699 {
700 return false;
701 }
702
703 public void setTransient(boolean newTransientValue)
704 {
705 // No op
706 }
707
708 public void restoreState(FacesContext context, Object state)
709 {
710 Object[] listAsMap = (Object[]) state;
711 for (int cnt = 0; cnt < listAsMap.length; cnt += 2)
712 {
713 this.put((K) listAsMap[cnt], (V) UIComponentBase
714 .restoreAttachedState(context, listAsMap[cnt + 1]));
715 }
716 }
717
718 public Object saveState(FacesContext context)
719 {
720 int cnt = 0;
721 Object[] mapArr = new Object[this.size() * 2];
722 for (Map.Entry<K, V> entry : this.entrySet())
723 {
724 mapArr[cnt] = entry.getKey();
725 Object value = entry.getValue();
726
727 if (value instanceof StateHolder ||
728 value instanceof List ||
729 !(value instanceof Serializable))
730 {
731 mapArr[cnt + 1] = saveAttachedState(context, value);
732 }
733 else
734 {
735 mapArr[cnt + 1] = value;
736 }
737 cnt += 2;
738 }
739 return mapArr;
740 }
741 }
742
743 /**
744 * Map used to keep track of list changes
745 */
746 static class InternalDeltaListMap<K, V> extends InternalMap<K, V>
747 {
748
749 public InternalDeltaListMap()
750 {
751 super();
752 }
753
754 public InternalDeltaListMap(int initialCapacity, float loadFactor)
755 {
756 super(initialCapacity, loadFactor);
757 }
758
759 public InternalDeltaListMap(int initialSize)
760 {
761 super(initialSize);
762 }
763
764 public InternalDeltaListMap(Map<? extends K, ? extends V> m)
765 {
766 super(m);
767 }
768 }
769
770 static class InternalList<T> extends ArrayList<T> implements StateHolder
771 {
772 public InternalList()
773 {
774 super();
775 }
776
777 public InternalList(Collection<? extends T> c)
778 {
779 super(c);
780 }
781
782 public InternalList(int initialSize)
783 {
784 super(initialSize);
785 }
786
787 public boolean isTransient()
788 {
789 return false;
790 }
791
792 public void setTransient(boolean newTransientValue)
793 {
794 }
795
796 public void restoreState(FacesContext context, Object state)
797 {
798 Object[] listAsArr = (Object[]) state;
799 //since all other options would mean dual iteration
800 //we have to do it the hard way
801 for (Object elem : listAsArr)
802 {
803 add((T) restoreAttachedState(context, elem));
804 }
805 }
806
807 public Object saveState(FacesContext context)
808 {
809 Object[] values = new Object[size()];
810 for (int i = 0; i < size(); i++)
811 {
812 Object value = get(i);
813
814 if (value instanceof StateHolder ||
815 value instanceof List ||
816 !(value instanceof Serializable))
817 {
818 values[i] = saveAttachedState(context, value);
819 }
820 else
821 {
822 values[i] = value;
823 }
824 }
825 return values;
826 }
827 }
828
829 private static Object saveAttachedState(FacesContext context, Object attachedObject)
830 {
831 if (context == null)
832 {
833 throw new NullPointerException ("context");
834 }
835
836 if (attachedObject == null)
837 {
838 return null;
839 }
840 // StateHolder interface should take precedence over
841 // List children
842 if (attachedObject instanceof StateHolder)
843 {
844 StateHolder holder = (StateHolder) attachedObject;
845 if (holder.isTransient())
846 {
847 return null;
848 }
849
850 return new _AttachedStateWrapper(attachedObject.getClass(), holder.saveState(context));
851 }
852 else if (attachedObject instanceof List)
853 {
854 List<Object> lst = new ArrayList<Object>(((List<?>) attachedObject).size());
855 for (Object item : (List<?>) attachedObject)
856 {
857 if (item != null)
858 {
859 lst.add(saveAttachedState(context, item));
860 }
861 }
862
863 return new _AttachedListStateWrapper(lst);
864 }
865 else if (attachedObject instanceof Serializable)
866 {
867 return attachedObject;
868 }
869 else
870 {
871 return new _AttachedStateWrapper(attachedObject.getClass(), null);
872 }
873 }
874
875 private static Object restoreAttachedState(FacesContext context, Object stateObj) throws IllegalStateException
876 {
877 if (context == null)
878 {
879 throw new NullPointerException("context");
880 }
881 if (stateObj == null)
882 {
883 return null;
884 }
885 if (stateObj instanceof _AttachedListStateWrapper)
886 {
887 List<Object> lst = ((_AttachedListStateWrapper) stateObj).getWrappedStateList();
888 List<Object> restoredList = new ArrayList<Object>(lst.size());
889 for (Object item : lst)
890 {
891 restoredList.add(restoreAttachedState(context, item));
892 }
893 return restoredList;
894 }
895 else if (stateObj instanceof _AttachedStateWrapper)
896 {
897 Class<?> clazz = ((_AttachedStateWrapper) stateObj).getClazz();
898 Object restoredObject;
899 try
900 {
901 restoredObject = clazz.newInstance();
902 }
903 catch (InstantiationException e)
904 {
905 throw new RuntimeException("Could not restore StateHolder of type " + clazz.getName()
906 + " (missing no-args constructor?)", e);
907 }
908 catch (IllegalAccessException e)
909 {
910 throw new RuntimeException(e);
911 }
912 if (restoredObject instanceof StateHolder)
913 {
914 _AttachedStateWrapper wrapper = (_AttachedStateWrapper) stateObj;
915 Object wrappedState = wrapper.getWrappedStateObject();
916
917 StateHolder holder = (StateHolder) restoredObject;
918 holder.restoreState(context, wrappedState);
919 }
920 return restoredObject;
921 }
922 else
923 {
924 return stateObj;
925 }
926 }
927 }