Coverage Report - javax.faces.component.behavior._DeltaStateHelper
 
Classes in this File Line Coverage Branch Coverage Complexity
_DeltaStateHelper
0%
0/209
0%
0/130
3.487
_DeltaStateHelper$InternalDeltaListMap
0%
0/8
N/A
3.487
_DeltaStateHelper$InternalList
0%
0/19
0%
0/10
3.487
_DeltaStateHelper$InternalMap
0%
0/25
0%
0/10
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  
 }