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