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: 1352587 $ $Date: 2012-06-21 11:03:33 -0500 (Thu, 21 Jun 2012) $
143 */
144 class _DeltaStateHelper implements StateHelper, TransientStateHelper, TransientStateHolder
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<Object, Object> _transientState;
166
167 //private Map<Serializable, Object> _initialState;
168 private Object[] _initialState;
169
170 /**
171 * This map keep track of StateHolder keys, to be saved when
172 * saveState is called.
173 */
174 //private Set<Serializable> _stateHolderKeys;
175
176 private boolean _transient = false;
177
178 public _DeltaStateHelper(UIComponent component)
179 {
180 super();
181 this._component = component;
182 _fullState = new HashMap<Serializable, Object>();
183 _deltas = null;
184 _transientState = null;
185 //_stateHolderKeys = new HashSet<Serializable>();
186 }
187
188 /**
189 * Used to create delta map on demand
190 *
191 * @return
192 */
193 private boolean _createDeltas()
194 {
195 if (isInitialStateMarked())
196 {
197 if (_deltas == null)
198 {
199 _deltas = new HashMap<Serializable, Object>(2);
200 }
201 return true;
202 }
203
204 return false;
205 }
206
207 protected boolean isInitialStateMarked()
208 {
209 return _component.initialStateMarked();
210 }
211
212 public void add(Serializable key, Object value)
213 {
214 if (_createDeltas())
215 {
216 //Track delta case
217 Map<Object, Boolean> deltaListMapValues = (Map<Object, Boolean>) _deltas
218 .get(key);
219 if (deltaListMapValues == null)
220 {
221 deltaListMapValues = new InternalDeltaListMap<Object, Boolean>(
222 3);
223 _deltas.put(key, deltaListMapValues);
224 }
225 deltaListMapValues.put(value, Boolean.TRUE);
226 }
227
228 //Handle change on full map
229 List<Object> fullListValues = (List<Object>) _fullState.get(key);
230 if (fullListValues == null)
231 {
232 fullListValues = new InternalList<Object>(3);
233 _fullState.put(key, fullListValues);
234 }
235 fullListValues.add(value);
236 }
237
238 public Object eval(Serializable key)
239 {
240 Object returnValue = _fullState.get(key);
241 if (returnValue != null)
242 {
243 return returnValue;
244 }
245 ValueExpression expression = _component.getValueExpression(key
246 .toString());
247 if (expression != null)
248 {
249 return expression.getValue(_component.getFacesContext()
250 .getELContext());
251 }
252 return null;
253 }
254
255 public Object eval(Serializable key, Object defaultValue)
256 {
257 Object returnValue = _fullState.get(key);
258 if (returnValue != null)
259 {
260 return returnValue;
261 }
262 ValueExpression expression = _component.getValueExpression(key
263 .toString());
264 if (expression != null)
265 {
266 return expression.getValue(_component.getFacesContext()
267 .getELContext());
268 }
269 return defaultValue;
270 }
271
272 public Object get(Serializable key)
273 {
274 return _fullState.get(key);
275 }
276
277 public Object put(Serializable key, Object value)
278 {
279 Object returnValue = null;
280 if (_createDeltas())
281 {
282 if (_deltas.containsKey(key))
283 {
284 returnValue = _deltas.put(key, value);
285 _fullState.put(key, value);
286 }
287 else if (value == null && !_fullState.containsKey(key))
288 {
289 returnValue = null;
290 }
291 else
292 {
293 _deltas.put(key, value);
294 returnValue = _fullState.put(key, value);
295 }
296 }
297 else
298 {
299 /*
300 if (value instanceof StateHolder)
301 {
302 _stateHolderKeys.add(key);
303 }
304 */
305 returnValue = _fullState.put(key, value);
306 }
307 return returnValue;
308 }
309
310 public Object put(Serializable key, String mapKey, Object value)
311 {
312 boolean returnSet = false;
313 Object returnValue = null;
314 if (_createDeltas())
315 {
316 //Track delta case
317 Map<String, Object> mapValues = (Map<String, Object>) _deltas
318 .get(key);
319 if (mapValues == null)
320 {
321 mapValues = new InternalMap<String, Object>();
322 _deltas.put(key, mapValues);
323 }
324 if (mapValues.containsKey(mapKey))
325 {
326 returnValue = mapValues.put(mapKey, value);
327 returnSet = true;
328 }
329 else
330 {
331 mapValues.put(mapKey, value);
332 }
333 }
334
335 //Handle change on full map
336 Map<String, Object> mapValues = (Map<String, Object>) _fullState
337 .get(key);
338 if (mapValues == null)
339 {
340 mapValues = new InternalMap<String, Object>();
341 _fullState.put(key, mapValues);
342 }
343 if (returnSet)
344 {
345 mapValues.put(mapKey, value);
346 }
347 else
348 {
349 returnValue = mapValues.put(mapKey, value);
350 }
351 return returnValue;
352 }
353
354 public Object remove(Serializable key)
355 {
356 Object returnValue = null;
357 if (_createDeltas())
358 {
359 if (_deltas.containsKey(key))
360 {
361 // Keep track of the removed values using key/null pair on the delta map
362 returnValue = _deltas.put(key, null);
363 _fullState.remove(key);
364 }
365 else
366 {
367 // Keep track of the removed values using key/null pair on the delta map
368 _deltas.put(key, null);
369 returnValue = _fullState.remove(key);
370 }
371 }
372 else
373 {
374 returnValue = _fullState.remove(key);
375 }
376 return returnValue;
377 }
378
379 public Object remove(Serializable key, Object valueOrKey)
380 {
381 // Comment by lu4242 : The spec javadoc says if it is a Collection
382 // or Map deal with it. But the intention of this method is work
383 // with add(?,?) and put(?,?,?), this ones return instances of
384 // InternalMap and InternalList to prevent mixing, so to be
385 // consistent we'll cast to those classes here.
386
387 Object collectionOrMap = _fullState.get(key);
388 Object returnValue = null;
389 if (collectionOrMap instanceof InternalMap)
390 {
391 if (_createDeltas())
392 {
393 returnValue = _removeValueOrKeyFromMap(_deltas, key,
394 valueOrKey, true);
395 _removeValueOrKeyFromMap(_fullState, key, valueOrKey, false);
396 }
397 else
398 {
399 returnValue = _removeValueOrKeyFromMap(_fullState, key,
400 valueOrKey, false);
401 }
402 }
403 else if (collectionOrMap instanceof InternalList)
404 {
405 if (_createDeltas())
406 {
407 returnValue = _removeValueOrKeyFromCollectionDelta(_deltas,
408 key, valueOrKey);
409 _removeValueOrKeyFromCollection(_fullState, key, valueOrKey);
410 }
411 else
412 {
413 returnValue = _removeValueOrKeyFromCollection(_fullState, key,
414 valueOrKey);
415 }
416 }
417 return returnValue;
418 }
419
420 private static Object _removeValueOrKeyFromCollectionDelta(
421 Map<Serializable, Object> stateMap, Serializable key,
422 Object valueOrKey)
423 {
424 Object returnValue = null;
425 Map<Object, Boolean> c = (Map<Object, Boolean>) stateMap.get(key);
426 if (c != null)
427 {
428 if (c.containsKey(valueOrKey))
429 {
430 returnValue = valueOrKey;
431 }
432 c.put(valueOrKey, Boolean.FALSE);
433 }
434 return returnValue;
435 }
436
437 private static Object _removeValueOrKeyFromCollection(
438 Map<Serializable, Object> stateMap, Serializable key,
439 Object valueOrKey)
440 {
441 Object returnValue = null;
442 Collection c = (Collection) stateMap.get(key);
443 if (c != null)
444 {
445 if (c.remove(valueOrKey))
446 {
447 returnValue = valueOrKey;
448 }
449 if (c.isEmpty())
450 {
451 stateMap.remove(key);
452 }
453 }
454 return returnValue;
455 }
456
457 private static Object _removeValueOrKeyFromMap(
458 Map<Serializable, Object> stateMap, Serializable key,
459 Object valueOrKey, boolean delta)
460 {
461 if (valueOrKey == null)
462 {
463 return null;
464 }
465
466 Object returnValue = null;
467 Map<String, Object> map = (Map<String, Object>) stateMap.get(key);
468 if (map != null)
469 {
470 if (delta)
471 {
472 // Keep track of the removed values using key/null pair on the delta map
473 returnValue = map.put((String) valueOrKey, null);
474 }
475 else
476 {
477 returnValue = map.remove(valueOrKey);
478 }
479
480 if (map.isEmpty())
481 {
482 //stateMap.remove(key);
483 stateMap.put(key, null);
484 }
485 }
486 return returnValue;
487 }
488
489 public boolean isTransient()
490 {
491 return _transient;
492 }
493
494 /**
495 * Serializing cod
496 * the serialized data structure consists of key value pairs unless the value itself is an internal array
497 * or a map in case of an internal array or map the value itself is another array with its initial value
498 * myfaces.InternalArray, myfaces.internalMap
499 *
500 * the internal Array is then mapped to another array
501 *
502 * the internal Map again is then mapped to a map with key value pairs
503 *
504 *
505 */
506 public Object saveState(FacesContext context)
507 {
508 Map serializableMap = (isInitialStateMarked()) ? _deltas : _fullState;
509
510 if (_initialState != null && _deltas != null && !_deltas.isEmpty()
511 && isInitialStateMarked())
512 {
513 // Before save the state, check if the property was changed from the
514 // initial state value. If the property was changed but it has the
515 // same value from the one in the initial state, we can remove it
516 // from delta, because when the view is built again, it will be
517 // restored to the same state. This check suppose some additional
518 // map.get() calls when saving the state, but using it only in properties
519 // that are expected to change over lifecycle (value, localValueSet,
520 // submittedValue, valid), is worth to do it, because those ones
521 // always generated delta changes.
522 for (int i = 0; i < _initialState.length; i+=2)
523 {
524 Serializable key = (Serializable) _initialState[i];
525 Object defaultValue = _initialState[i+1];
526
527 // Check only if there is delta state for that property, in other
528 // case it is not necessary. Remember it is possible to have
529 // null values inside the Map.
530 if (_deltas.containsKey(key))
531 {
532 Object deltaValue = _deltas.get(key);
533 if (deltaValue == null && defaultValue == null)
534 {
535 _deltas.remove(key);
536 if (_deltas.isEmpty())
537 {
538 break;
539 }
540 }
541 if (deltaValue != null && deltaValue.equals(defaultValue))
542 {
543 _deltas.remove(key);
544 if (_deltas.isEmpty())
545 {
546 break;
547 }
548 }
549 }
550 }
551 }
552 if (serializableMap == null || serializableMap.size() == 0)
553 {
554 return null;
555 }
556
557 /*
558 int stateHolderKeyCount = 0;
559 if (isInitalStateMarked())
560 {
561 for (Iterator<Serializable> it = _stateHolderKeys.iterator(); it.hasNext();)
562 {
563 Serializable key = it.next();
564 if (!_deltas.containsKey(key))
565 {
566 stateHolderKeyCount++;
567 }
568 }
569 }*/
570
571 Map.Entry<Serializable, Object> entry;
572 //entry == key, value, key, value
573 Object[] retArr = new Object[serializableMap.entrySet().size() * 2];
574 //Object[] retArr = new Object[serializableMap.entrySet().size() * 2 + stateHolderKeyCount];
575
576 Iterator<Map.Entry<Serializable, Object>> it = serializableMap
577 .entrySet().iterator();
578 int cnt = 0;
579 while (it.hasNext())
580 {
581 entry = it.next();
582 retArr[cnt] = entry.getKey();
583
584 Object value = entry.getValue();
585
586 // The condition in which the call to saveAttachedState
587 // is to handle List, StateHolder or non Serializable instances.
588 // we check it here, to prevent unnecessary calls.
589 if (value instanceof StateHolder ||
590 value instanceof List ||
591 !(value instanceof Serializable))
592 {
593 Object savedValue = UIComponentBase.saveAttachedState(context,
594 value);
595 retArr[cnt + 1] = savedValue;
596 }
597 else
598 {
599 retArr[cnt + 1] = value;
600 }
601 cnt += 2;
602 }
603
604 /*
605 if (isInitalStateMarked())
606 {
607 for (Iterator<Serializable> it2 = _stateHolderKeys.iterator(); it.hasNext();)
608 {
609 Serializable key = it2.next();
610 if (!_deltas.containsKey(key))
611 {
612 retArr[cnt] = key;
613 Object value = _fullState.get(key);
614 if (value instanceof PartialStateHolder)
615 {
616 //Could contain delta, save it as _AttachedDeltaState
617 PartialStateHolder holder = (PartialStateHolder) value;
618 if (holder.isTransient())
619 {
620 retArr[cnt + 1] = null;
621 }
622 else
623 {
624 retArr[cnt + 1] = new _AttachedDeltaWrapper(value.getClass(), holder.saveState(context));
625 }
626 }
627 else
628 {
629 //Save everything
630 retArr[cnt + 1] = UIComponentBase.saveAttachedState(context, _fullState.get(key));
631 }
632 cnt += 2;
633 }
634 }
635 }
636 */
637 return retArr;
638 }
639
640 public void restoreState(FacesContext context, Object state)
641 {
642 if (state == null)
643 {
644 return;
645 }
646
647 Object[] serializedState = (Object[]) state;
648
649 if (!isInitialStateMarked() && !_fullState.isEmpty())
650 {
651 _fullState.clear();
652 if(_deltas != null)
653 {
654 _deltas.clear();
655 }
656 }
657
658 for (int cnt = 0; cnt < serializedState.length; cnt += 2)
659 {
660 Serializable key = (Serializable) serializedState[cnt];
661 Object savedValue = UIComponentBase.restoreAttachedState(context,
662 serializedState[cnt + 1]);
663
664 if (isInitialStateMarked())
665 {
666 if (savedValue instanceof InternalDeltaListMap)
667 {
668 for (Map.Entry<Object, Boolean> mapEntry : ((Map<Object, Boolean>) savedValue)
669 .entrySet())
670 {
671 boolean addOrRemove = mapEntry.getValue();
672 if (addOrRemove)
673 {
674 //add
675 this.add(key, mapEntry.getKey());
676 }
677 else
678 {
679 //remove
680 this.remove(key, mapEntry.getKey());
681 }
682 }
683 }
684 else if (savedValue instanceof InternalMap)
685 {
686 for (Map.Entry<String, Object> mapEntry : ((Map<String, Object>) savedValue)
687 .entrySet())
688 {
689 this.put(key, mapEntry.getKey(), mapEntry.getValue());
690 }
691 }
692 /*
693 else if (savedValue instanceof _AttachedDeltaWrapper)
694 {
695 _AttachedStateWrapper wrapper = (_AttachedStateWrapper) savedValue;
696 //Restore delta state
697 ((PartialStateHolder)_fullState.get(key)).restoreState(context, wrapper.getWrappedStateObject());
698 //Add this key as StateHolder key
699 _stateHolderKeys.add(key);
700 }
701 */
702 else
703 {
704 put(key, savedValue);
705 }
706 }
707 else
708 {
709 put(key, savedValue);
710 }
711 }
712 }
713
714 public void setTransient(boolean transientValue)
715 {
716 _transient = transientValue;
717 }
718
719 //We use our own data structures just to make sure
720 //nothing gets mixed up internally
721 static class InternalMap<K, V> extends HashMap<K, V> implements StateHolder
722 {
723 public InternalMap()
724 {
725 super();
726 }
727
728 public InternalMap(int initialCapacity, float loadFactor)
729 {
730 super(initialCapacity, loadFactor);
731 }
732
733 public InternalMap(Map<? extends K, ? extends V> m)
734 {
735 super(m);
736 }
737
738 public InternalMap(int initialSize)
739 {
740 super(initialSize);
741 }
742
743 public boolean isTransient()
744 {
745 return false;
746 }
747
748 public void setTransient(boolean newTransientValue)
749 {
750 // No op
751 }
752
753 public void restoreState(FacesContext context, Object state)
754 {
755 Object[] listAsMap = (Object[]) state;
756 for (int cnt = 0; cnt < listAsMap.length; cnt += 2)
757 {
758 this.put((K) listAsMap[cnt], (V) UIComponentBase
759 .restoreAttachedState(context, listAsMap[cnt + 1]));
760 }
761 }
762
763 public Object saveState(FacesContext context)
764 {
765 int cnt = 0;
766 Object[] mapArr = new Object[this.size() * 2];
767 for (Map.Entry<K, V> entry : this.entrySet())
768 {
769 mapArr[cnt] = entry.getKey();
770 Object value = entry.getValue();
771
772 if (value instanceof StateHolder ||
773 value instanceof List ||
774 !(value instanceof Serializable))
775 {
776 mapArr[cnt + 1] = UIComponentBase.saveAttachedState(context, value);
777 }
778 else
779 {
780 mapArr[cnt + 1] = value;
781 }
782 cnt += 2;
783 }
784 return mapArr;
785 }
786 }
787
788 /**
789 * Map used to keep track of list changes
790 */
791 static class InternalDeltaListMap<K, V> extends InternalMap<K, V>
792 {
793
794 public InternalDeltaListMap()
795 {
796 super();
797 }
798
799 public InternalDeltaListMap(int initialCapacity, float loadFactor)
800 {
801 super(initialCapacity, loadFactor);
802 }
803
804 public InternalDeltaListMap(int initialSize)
805 {
806 super(initialSize);
807 }
808
809 public InternalDeltaListMap(Map<? extends K, ? extends V> m)
810 {
811 super(m);
812 }
813 }
814
815 static class InternalList<T> extends ArrayList<T> implements StateHolder
816 {
817 public InternalList()
818 {
819 super();
820 }
821
822 public InternalList(Collection<? extends T> c)
823 {
824 super(c);
825 }
826
827 public InternalList(int initialSize)
828 {
829 super(initialSize);
830 }
831
832 public boolean isTransient()
833 {
834 return false;
835 }
836
837 public void setTransient(boolean newTransientValue)
838 {
839 }
840
841 public void restoreState(FacesContext context, Object state)
842 {
843 Object[] listAsArr = (Object[]) state;
844 //since all other options would mean dual iteration
845 //we have to do it the hard way
846 for (Object elem : listAsArr)
847 {
848 add((T) UIComponentBase.restoreAttachedState(context, elem));
849 }
850 }
851
852 public Object saveState(FacesContext context)
853 {
854 Object[] values = new Object[size()];
855 for (int i = 0; i < size(); i++)
856 {
857 Object value = get(i);
858
859 if (value instanceof StateHolder ||
860 value instanceof List ||
861 !(value instanceof Serializable))
862 {
863 values[i] = UIComponentBase.saveAttachedState(context, value);
864 }
865 else
866 {
867 values[i] = value;
868 }
869 }
870 return values;
871 }
872 }
873
874 public Object getTransient(Object key)
875 {
876 return (_transientState == null) ? null : _transientState.get(key);
877 }
878
879 public Object getTransient(Object key, Object defaultValue)
880 {
881 Object returnValue = (_transientState == null) ? null : _transientState.get(key);
882 if (returnValue != null)
883 {
884 return returnValue;
885 }
886 return defaultValue;
887 }
888
889 public Object putTransient(Object key, Object value)
890 {
891 if (_transientState == null)
892 {
893 _transientState = new HashMap<Object, Object>();
894 }
895 return _transientState.put(key, value);
896 }
897
898 @SuppressWarnings("unchecked")
899 public void restoreTransientState(FacesContext context, Object state)
900 {
901 _transientState = (Map<Object, Object>) state;
902 }
903
904 public Object saveTransientState(FacesContext context)
905 {
906 return _transientState;
907 }
908
909 public void markPropertyInInitialState(Object[] defaultInitialState)
910 {
911 // Check if in the fullState, one of the default properties were changed
912 boolean canApplyDefaultInitialState = true;
913 for (int i = 0; i < defaultInitialState.length; i+=2)
914 {
915 Serializable key = (Serializable) defaultInitialState[i];
916 if (_fullState.containsKey(key))
917 {
918 canApplyDefaultInitialState = false;
919 break;
920 }
921 }
922 if (canApplyDefaultInitialState)
923 {
924 // Most of the times the defaultInitialState is used.
925 _initialState = defaultInitialState;
926 }
927 else
928 {
929 // recalculate it
930 Object[] initialState = new Object[defaultInitialState.length];
931 for (int i = 0; i < defaultInitialState.length; i+=2)
932 {
933 Serializable key = (Serializable) defaultInitialState[i];
934 initialState[i] = key;
935 if (_fullState.containsKey(key))
936 {
937 initialState[i+1] = _fullState.get(key);
938 }
939 else
940 {
941 initialState[i+1] = defaultInitialState[i+1];
942 }
943 }
944 _initialState = initialState;
945 }
946 }
947 }