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