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;
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.context.FacesContext;
31  
32  /**
33   * A delta enabled state holder implementing the StateHolder Interface. 
34   * <p>
35   * Components implementing the PartalStateHolder interface have an initial state
36   * and delta states, the initial state is the one holding all root values
37   * and deltas store differences to the initial states
38   * </p>
39   * <p>
40   * For components not implementing partial state saving only the initial states are
41   * of importance, everything is stored and restored continously there
42   * </p> 
43   * <p>
44   * The state helper seems to have three internal storage mechanisms:
45   * one being a list which stores plain values,
46   * one being a key value pair which stores key values in maps
47   * add serves the plain list type while put serves the 
48   * key value type, 
49   * the third is the value which has to be stored plainly as is!
50   * </p>
51   * In other words, this map can be seen as a composite map. It has two maps: 
52   * initial state map and delta map.
53   * <p> 
54   * If delta map is used (method component.initialStateMarked() ), 
55   * base or initial state map cannot be changed, since all changes 
56   * should be tracked on delta map.
57   * </p>
58   * <p> 
59   * The intention of this class is just hold property values
60   * and do a clean separation between initial state and delta.
61   * </p>
62   * <p>
63   * The code from this class comes from a refactor of 
64   * org.apache.myfaces.trinidad.bean.util.PropertyHashMap
65   * </p>
66   * <p>
67   * The context from this class comes and that should be taken into account
68   * is this:
69   * </p>
70   * <p> 
71   * First request:
72   * </p>
73   * <ul>
74   *   <li> A new template is created (using 
75   *   javax.faces.view.ViewDeclarationLanguage.buildView method)
76   *   and component.markInitialState is called from its related TagHandler classes 
77   *  (see javax.faces.view.facelets.ComponentHandler ).
78   *   When this method is executed, the component tree was populated from the values
79   *   set in the facelet abstract syntax tree (or in other words composition of 
80   *   facelets templates). </li>
81   *   <li> From this point all updates on the variables are considered "delta". </li>
82   *   <li> SaveState, if initialStateMarked is true, only delta is saved. </li>
83   * </ul>
84   * <p>
85   * Second request (and next ones)
86   * </p>
87   * <ul>
88   *   <li> A new template is created and component.markInitialState is called from
89   *   its related TagHandler classes again. In this way, components like c:forEach 
90   *   or c:if, that add or remove components could notify about this and handle 
91   *   them properly (see javax.faces.view.StateManagementStrategy). Note that a 
92   *   component restored using this method is no different as the same component 
93   *   at the first request at the same time. </li>
94   *   <li> A call for restoreState is done, passing the delta as object value. If no 
95   *   delta, the state is complete and no call is triggered. </li>
96   *   <li> Lifecycle occur, changing the necessary stuff. </li>
97   *   <li> SaveState, if initialStateMarked is true, only delta is saved. </li>
98   * </ul>
99   * <p>
100  * From the previous analysis, the following conclusions arise:
101  * <ul>
102  *   <li>This class only needs to keep track of delta changes, so when 
103  *   restoreState/saveState is called, the right objects are passed.</li>
104  *   <li>UIComponent.clearInitialState is used to reset the partial
105  *   state holder to a non delta state, so the state to be saved by
106  *   saveState is no longer a delta instead is a full state. If a call
107  *   to clearInitialState occur it is not expected a call for 
108  *   UIComponent.markInitialState occur on the current request.</li>
109  *   <li>The state is handled in the same way on UIData, so components
110  *   inside UIData share its state on all rows. There is no way to save 
111  *   delta per row.</li>
112  *   <li>The map backed by method put(Serializable,String,Object) is
113  *   a replacement of UIComponentBase.attributesMap and UIComponent.bindings map.
114  *   Note that on jsf 1.2, instances saved on attributesMap should not be
115  *   StateHolder, but on jsf 2.0 it is possible to have it. PartialStateHolder
116  *   instances are not handled in this map, or in other words delta state is not
117  *   handled in this classes (markInitialState and clearInitialState is not propagated).</li>
118  *   <li>The list backed by method add(Serializable,Object) should be (is not) a 
119  *   replacement of UIComponentBase.facesListeners, but note that StateHelper
120  *   does not implement PartialStateHolder, and facesListener could have instances
121  *   of that class that needs to be notified when UIComponent.markInitialState or
122  *   UIComponent.clearInitialState is called, or in other words facesListeners
123  *   should deal with PartialStateHolder instances.</li>
124  *   <li>The list backed by method add(Serializable,Object) is 
125  *   a replacement of UIViewRoot.phaseListeners list. Note that instances of
126  *   PhaseListener are not expected to implement StateHolder or PartialStateHolder.</li>
127  * </ul>
128  * </p>
129  * <p>
130  * NOTE: The current implementation of StateHelper on RI does not handle
131  * stateHolder values internally. To prevent problems when developers create
132  * custom components we should do this too. But anyway, the code that 
133  * handle this case should be let here as comment, if some day this feature
134  * is provided. Note than stateHolder aware properties like converter,
135  * validator or listeners should deal with StateHolder or PartialStateHolder
136  * on component classes. 
137  * 
138  * </p>
139  *
140  * @author Werner Punz
141  * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
142  * @version $Rev: 1352586 $ $Date: 2012-06-21 11:03:15 -0500 (Thu, 21 Jun 2012) $
143  */
144 class _DeltaStateHelper implements StateHelper
145 {
146 
147     /**
148      * We need to hold a component instance because:
149      * 
150      * - The component is the one who knows if we are on initial or delta mode
151      * - eval assume calls to component.ValueExpression
152      */
153     private UIComponent _component;
154 
155     /**
156      * This map holds the full current state
157      */
158     private Map<Serializable, Object> _fullState;
159 
160     /**
161      * This map only keep track of delta changes to be saved
162      */
163     private Map<Serializable, Object> _deltas;
164     
165     //private Map<Serializable, Object> _initialState;
166     private Object[] _initialState;
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(UIComponent component)
177     {
178         super();
179         this._component = component;
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 _component.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 = _component.getValueExpression(key
243                 .toString());
244         if (expression != null)
245         {
246             return expression.getValue(_component.getFacesContext()
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 = _component.getValueExpression(key
260                 .toString());
261         if (expression != null)
262         {
263             return expression.getValue(_component.getFacesContext()
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 (_initialState != null && _deltas != null && !_deltas.isEmpty()
508             && isInitialStateMarked())
509         {
510             // Before save the state, check if the property was changed from the
511             // initial state value. If the property was changed but it has the
512             // same value from the one in the initial state, we can remove it
513             // from delta, because when the view is built again, it will be
514             // restored to the same state. This check suppose some additional
515             // map.get() calls when saving the state, but using it only in properties
516             // that are expected to change over lifecycle (value, localValueSet,
517             // submittedValue, valid), is worth to do it, because those ones
518             // always generated delta changes.
519             for (int i = 0; i < _initialState.length; i+=2)
520             {
521                 Serializable key = (Serializable) _initialState[i];
522                 Object defaultValue = _initialState[i+1];
523                 
524                 // Check only if there is delta state for that property, in other
525                 // case it is not necessary. Remember it is possible to have
526                 // null values inside the Map.
527                 if (_deltas.containsKey(key))
528                 {
529                     Object deltaValue = _deltas.get(key);
530                     if (deltaValue == null && defaultValue == null)
531                     {
532                         _deltas.remove(key);
533                         if (_deltas.isEmpty())
534                         {
535                             break;
536                         }
537                     }
538                     if (deltaValue != null && deltaValue.equals(defaultValue))
539                     {
540                         _deltas.remove(key);
541                         if (_deltas.isEmpty())
542                         {
543                             break;
544                         }
545                     }
546                 }
547             }
548         }
549         if (serializableMap == null || serializableMap.size() == 0)
550         {
551             return null;
552         }
553         
554         /*
555         int stateHolderKeyCount = 0;
556         if (isInitalStateMarked())
557         {
558             for (Iterator<Serializable> it = _stateHolderKeys.iterator(); it.hasNext();)
559             {
560                 Serializable key = it.next();
561                 if (!_deltas.containsKey(key))
562                 {
563                     stateHolderKeyCount++;
564                 }
565             }
566         }*/
567         
568         Map.Entry<Serializable, Object> entry;
569         //entry == key, value, key, value
570         Object[] retArr = new Object[serializableMap.entrySet().size() * 2];
571         //Object[] retArr = new Object[serializableMap.entrySet().size() * 2 + stateHolderKeyCount]; 
572 
573         Iterator<Map.Entry<Serializable, Object>> it = serializableMap
574                 .entrySet().iterator();
575         int cnt = 0;
576         while (it.hasNext())
577         {
578             entry = it.next();
579             retArr[cnt] = entry.getKey();
580 
581             Object value = entry.getValue();
582             
583             // The condition in which the call to saveAttachedState
584             // is to handle List, StateHolder or non Serializable instances.
585             // we check it here, to prevent unnecessary calls.
586             if (value instanceof StateHolder ||
587                 value instanceof List ||
588                 !(value instanceof Serializable))
589             {
590                 Object savedValue = UIComponentBase.saveAttachedState(context,
591                     value);
592                 retArr[cnt + 1] = savedValue;
593             }
594             else
595             {
596                 retArr[cnt + 1] = value;
597             }
598             cnt += 2;
599         }
600         
601         /*
602         if (isInitalStateMarked())
603         {
604             for (Iterator<Serializable> it2 = _stateHolderKeys.iterator(); it.hasNext();)
605             {
606                 Serializable key = it2.next();
607                 if (!_deltas.containsKey(key))
608                 {
609                     retArr[cnt] = key;
610                     Object value = _fullState.get(key);
611                     if (value instanceof PartialStateHolder)
612                     {
613                         //Could contain delta, save it as _AttachedDeltaState
614                         PartialStateHolder holder = (PartialStateHolder) value;
615                         if (holder.isTransient())
616                         {
617                             retArr[cnt + 1] = null;
618                         }
619                         else
620                         {
621                             retArr[cnt + 1] = new _AttachedDeltaWrapper(value.getClass(), holder.saveState(context));
622                         }
623                     }
624                     else
625                     {
626                         //Save everything
627                         retArr[cnt + 1] = UIComponentBase.saveAttachedState(context, _fullState.get(key));
628                     }
629                     cnt += 2;
630                 }
631             }
632         }
633         */       
634         return retArr;
635     }
636 
637     public void restoreState(FacesContext context, Object state)
638     {
639         if (state == null)
640             return;
641 
642         Object[] serializedState = (Object[]) state;
643         
644         if (!isInitialStateMarked() && !_fullState.isEmpty())
645         {
646             _fullState.clear();
647             if(_deltas != null)
648             {
649                 _deltas.clear();
650             }
651         }
652 
653         for (int cnt = 0; cnt < serializedState.length; cnt += 2)
654         {
655             Serializable key = (Serializable) serializedState[cnt];
656             Object savedValue = UIComponentBase.restoreAttachedState(context,
657                     serializedState[cnt + 1]);
658 
659             if (isInitialStateMarked())
660             {
661                 if (savedValue instanceof InternalDeltaListMap)
662                 {
663                     for (Map.Entry<Object, Boolean> mapEntry : ((Map<Object, Boolean>) savedValue)
664                             .entrySet())
665                     {
666                         boolean addOrRemove = mapEntry.getValue();
667                         if (addOrRemove)
668                         {
669                             //add
670                             this.add(key, mapEntry.getKey());
671                         }
672                         else
673                         {
674                             //remove
675                             this.remove(key, mapEntry.getKey());
676                         }
677                     }
678                 }
679                 else if (savedValue instanceof InternalMap)
680                 {
681                     for (Map.Entry<String, Object> mapEntry : ((Map<String, Object>) savedValue)
682                             .entrySet())
683                     {
684                         this.put(key, mapEntry.getKey(), mapEntry.getValue());
685                     }
686                 }
687                 /*
688                 else if (savedValue instanceof _AttachedDeltaWrapper)
689                 {
690                     _AttachedStateWrapper wrapper = (_AttachedStateWrapper) savedValue;
691                     //Restore delta state
692                     ((PartialStateHolder)_fullState.get(key)).restoreState(context, wrapper.getWrappedStateObject());
693                     //Add this key as StateHolder key 
694                     _stateHolderKeys.add(key);
695                 }
696                 */
697                 else
698                 {
699                     put(key, savedValue);
700                 }
701             }
702             else
703             {
704                 put(key, savedValue);
705             }
706         }
707     }
708 
709     public void setTransient(boolean transientValue)
710     {
711         _transient = transientValue;
712     }
713 
714     //We use our own data structures just to make sure
715     //nothing gets mixed up internally
716     static class InternalMap<K, V> extends HashMap<K, V> implements StateHolder
717     {
718         public InternalMap()
719         {
720             super();
721         }
722 
723         public InternalMap(int initialCapacity, float loadFactor)
724         {
725             super(initialCapacity, loadFactor);
726         }
727 
728         public InternalMap(Map<? extends K, ? extends V> m)
729         {
730             super(m);
731         }
732 
733         public InternalMap(int initialSize)
734         {
735             super(initialSize);
736         }
737 
738         public boolean isTransient()
739         {
740             return false;
741         }
742 
743         public void setTransient(boolean newTransientValue)
744         {
745             // No op
746         }
747 
748         public void restoreState(FacesContext context, Object state)
749         {
750             Object[] listAsMap = (Object[]) state;
751             for (int cnt = 0; cnt < listAsMap.length; cnt += 2)
752             {
753                 this.put((K) listAsMap[cnt], (V) UIComponentBase
754                         .restoreAttachedState(context, listAsMap[cnt + 1]));
755             }
756         }
757 
758         public Object saveState(FacesContext context)
759         {
760             int cnt = 0;
761             Object[] mapArr = new Object[this.size() * 2];
762             for (Map.Entry<K, V> entry : this.entrySet())
763             {
764                 mapArr[cnt] = entry.getKey();
765                 Object value = entry.getValue();
766                 
767                 if (value instanceof StateHolder ||
768                     value instanceof List ||
769                     !(value instanceof Serializable))
770                 {
771                     mapArr[cnt + 1] = UIComponentBase.saveAttachedState(context, value);
772                 }
773                 else
774                 {
775                     mapArr[cnt + 1] = value;
776                 }
777                 cnt += 2;
778             }
779             return mapArr;
780         }
781     }
782 
783     /**
784      * Map used to keep track of list changes 
785      */
786     static class InternalDeltaListMap<K, V> extends InternalMap<K, V>
787     {
788 
789         public InternalDeltaListMap()
790         {
791             super();
792         }
793 
794         public InternalDeltaListMap(int initialCapacity, float loadFactor)
795         {
796             super(initialCapacity, loadFactor);
797         }
798 
799         public InternalDeltaListMap(int initialSize)
800         {
801             super(initialSize);
802         }
803 
804         public InternalDeltaListMap(Map<? extends K, ? extends V> m)
805         {
806             super(m);
807         }
808     }
809 
810     static class InternalList<T> extends ArrayList<T> implements StateHolder
811     {
812         public InternalList()
813         {
814             super();
815         }
816 
817         public InternalList(Collection<? extends T> c)
818         {
819             super(c);
820         }
821 
822         public InternalList(int initialSize)
823         {
824             super(initialSize);
825         }
826 
827         public boolean isTransient()
828         {
829             return false;
830         }
831 
832         public void setTransient(boolean newTransientValue)
833         {
834         }
835 
836         public void restoreState(FacesContext context, Object state)
837         {
838             Object[] listAsArr = (Object[]) state;
839             //since all other options would mean dual iteration 
840             //we have to do it the hard way
841             for (Object elem : listAsArr)
842             {
843                 add((T) UIComponentBase.restoreAttachedState(context, elem));
844             }
845         }
846 
847         public Object saveState(FacesContext context)
848         {
849             Object[] values = new Object[size()];
850             for (int i = 0; i < size(); i++)
851             {
852                 Object value = get(i);
853                 
854                 if (value instanceof StateHolder ||
855                     value instanceof List ||
856                     !(value instanceof Serializable))
857                 {
858                     values[i] = UIComponentBase.saveAttachedState(context, value);
859                 }
860                 else
861                 {
862                     values[i] = value;
863                 }                
864             }
865             return values;
866         }
867     }
868 
869     public void markPropertyInInitialState(Object[] defaultInitialState)
870     {
871         // Check if in the fullState, one of the default properties were changed
872         boolean canApplyDefaultInitialState = true;
873         for (int i = 0; i < defaultInitialState.length; i+=2)
874         {
875             Serializable key = (Serializable) defaultInitialState[i];
876             if (_fullState.containsKey(key))
877             {
878                 canApplyDefaultInitialState = false;
879                 break;
880             }
881         }
882         if (canApplyDefaultInitialState)
883         {
884             // Most of the times the defaultInitialState is used.
885             _initialState = defaultInitialState;
886         }
887         else
888         {
889             // recalculate it
890             Object[] initialState = new Object[defaultInitialState.length];
891             for (int i = 0; i < defaultInitialState.length; i+=2)
892             {
893                 Serializable key = (Serializable) defaultInitialState[i];
894                 initialState[i] = key;
895                 if (_fullState.containsKey(key))
896                 {
897                     initialState[i+1] = _fullState.get(key);
898                 }
899                 else
900                 {
901                     initialState[i+1] = defaultInitialState[i+1];
902                 }
903             }
904             _initialState = initialState;
905         }
906     }
907 }