_DeltaStateHelper.java :  » J2EE » myfaces-core-2.0.0 » javax » faces » component » Java Open Source

Java Open Source » J2EE » myfaces core 2.0.0 
myfaces core 2.0.0 » javax » faces » component » _DeltaStateHelper.java
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package javax.faces.component;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.el.ValueExpression;
import javax.faces.context.FacesContext;

/**
 * A delta enabled state holder implementing the StateHolder Interface. 
 * <p>
 * Components implementing the PartalStateHolder interface have an initial state
 * and delta states, the initial state is the one holding all root values
 * and deltas store differences to the initial states
 * </p>
 * <p>
 * For components not implementing partial state saving only the initial states are
 * of importance, everything is stored and restored continously there
 * </p> 
 * <p>
 * The state helper seems to have three internal storage mechanisms:
 * one being a list which stores plain values,
 * one being a key value pair which stores key values in maps
 * add serves the plain list type while put serves the 
 * key value type, 
 * the third is the value which has to be stored plainly as is!
 * </p>
 * In other words, this map can be seen as a composite map. It has two maps: 
 * initial state map and delta map.
 * <p> 
 * If delta map is used (method component.initialStateMarked() ), 
 * base or initial state map cannot be changed, since all changes 
 * should be tracked on delta map.
 * </p>
 * <p> 
 * The intention of this class is just hold property values
 * and do a clean separation between initial state and delta.
 * </p>
 * <p>
 * The code from this class comes from a refactor of 
 * org.apache.myfaces.trinidad.bean.util.PropertyHashMap
 * </p>
 * <p>
 * The context from this class comes and that should be taken into account
 * is this:
 * </p>
 * <p> 
 * First request:
 * </p>
 * <ul>
 *   <li> A new template is created (using 
 *   javax.faces.view.ViewDeclarationLanguage.buildView method)
 *   and component.markInitialState is called from its related TagHandler classes 
 *  (see javax.faces.view.facelets.ComponentHandler ).
 *   When this method is executed, the component tree was populated from the values
 *   set in the facelet abstract syntax tree (or in other words composition of 
 *   facelets templates). </li>
 *   <li> From this point all updates on the variables are considered "delta". </li>
 *   <li> SaveState, if initialStateMarked is true, only delta is saved. </li>
 * </ul>
 * <p>
 * Second request (and next ones)
 * </p>
 * <ul>
 *   <li> A new template is created and component.markInitialState is called from
 *   its related TagHandler classes again. In this way, components like c:forEach 
 *   or c:if, that add or remove components could notify about this and handle 
 *   them properly (see javax.faces.view.StateManagementStrategy). Note that a 
 *   component restored using this method is no different as the same component 
 *   at the first request at the same time. </li>
 *   <li> A call for restoreState is done, passing the delta as object value. If no 
 *   delta, the state is complete and no call is triggered. </li>
 *   <li> Lifecycle occur, changing the necessary stuff. </li>
 *   <li> SaveState, if initialStateMarked is true, only delta is saved. </li>
 * </ul>
 * <p>
 * From the previous analysis, the following conclusions arise:
 * <ul>
 *   <li>This class only needs to keep track of delta changes, so when 
 *   restoreState/saveState is called, the right objects are passed.</li>
 *   <li>UIComponent.clearInitialState is used to reset the partial
 *   state holder to a non delta state, so the state to be saved by
 *   saveState is no longer a delta instead is a full state. If a call
 *   to clearInitialState occur it is not expected a call for 
 *   UIComponent.markInitialState occur on the current request.</li>
 *   <li>The state is handled in the same way on UIData, so components
 *   inside UIData share its state on all rows. There is no way to save 
 *   delta per row.</li>
 *   <li>The map backed by method put(Serializable,String,Object) is
 *   a replacement of UIComponentBase.attributesMap and UIComponent.bindings map.
 *   Note that on jsf 1.2, instances saved on attributesMap should not be
 *   StateHolder, but on jsf 2.0 it is possible to have it. PartialStateHolder
 *   instances are not handled in this map, or in other words delta state is not
 *   handled in this classes (markInitialState and clearInitialState is not propagated).</li>
 *   <li>The list backed by method add(Serializable,Object) should be (is not) a 
 *   replacement of UIComponentBase.facesListeners, but note that StateHelper
 *   does not implement PartialStateHolder, and facesListener could have instances
 *   of that class that needs to be notified when UIComponent.markInitialState or
 *   UIComponent.clearInitialState is called, or in other words facesListeners
 *   should deal with PartialStateHolder instances.</li>
 *   <li>The list backed by method add(Serializable,Object) is 
 *   a replacement of UIViewRoot.phaseListeners list. Note that instances of
 *   PhaseListener are not expected to implement StateHolder or PartialStateHolder.</li>
 * </ul>
 * </p>
 * <p>
 * NOTE: The current implementation of StateHelper on RI does not handle
 * stateHolder values internally. To prevent problems when developers create
 * custom components we should do this too. But anyway, the code that 
 * handle this case should be let here as comment, if some day this feature
 * is provided. Note than stateHolder aware properties like converter,
 * validator or listeners should deal with StateHolder or PartialStateHolder
 * on component classes. 
 * 
 * </p>
 *
 * @author Werner Punz
 * @author Leonardo Uribe (latest modification by $Author: lu4242 $)
 * @version $Rev: 881558 $ $Date: 2009-11-17 16:55:58 -0500 (Tue, 17 Nov 2009) $
 */
class _DeltaStateHelper implements StateHelper
{

    /**
     * We need to hold a component instance because:
     * 
     * - The component is the one who knows if we are on initial or delta mode
     * - eval assume calls to component.ValueExpression
     */
    private UIComponent _component;

    /**
     * This map holds the full current state
     */
    private Map<Serializable, Object> _fullState;

    /**
     * This map only keep track of delta changes to be saved
     */
    private Map<Serializable, Object> _deltas;
    
    /**
     * This map keep track of StateHolder keys, to be saved when
     * saveState is called. 
     */
    //private Set<Serializable> _stateHolderKeys;  

    private boolean _transient = false;

    public _DeltaStateHelper(UIComponent component)
    {
        super();
        this._component = component;
        _fullState = new HashMap<Serializable, Object>();
        _deltas = null;
        //_stateHolderKeys = new HashSet<Serializable>();
    }

    /**
     * Used to create delta map on demand
     * 
     * @return
     */
    private boolean _createDeltas()
    {
        if (isInitialStateMarked())
        {
            if (_deltas == null)
            {
                _deltas = new HashMap<Serializable, Object>(2);
            }
            return true;
        }

        return false;
    }
    
    protected boolean isInitialStateMarked()
    {
        return _component.initialStateMarked();
    }

    public void add(Serializable key, Object value)
    {
        if (_createDeltas())
        {
            //Track delta case
            Map<Object, Boolean> deltaListMapValues = (Map<Object, Boolean>) _deltas
                    .get(key);
            if (deltaListMapValues == null)
            {
                deltaListMapValues = new InternalDeltaListMap<Object, Boolean>(
                        3);
                _deltas.put(key, deltaListMapValues);
            }
            deltaListMapValues.put(value, Boolean.TRUE);
        }

        //Handle change on full map
        List<Object> fullListValues = (List<Object>) _fullState.get(key);
        if (fullListValues == null)
        {
            fullListValues = new InternalList<Object>(3);
            _fullState.put(key, fullListValues);
        }
        fullListValues.add(value);
    }

    public Object eval(Serializable key)
    {
        Object returnValue = _fullState.get(key);
        if (returnValue != null)
        {
            return returnValue;
        }
        ValueExpression expression = _component.getValueExpression(key
                .toString());
        if (expression != null)
        {
            return expression.getValue(_component.getFacesContext()
                    .getELContext());
        }
        return null;
    }

    public Object eval(Serializable key, Object defaultValue)
    {
        Object returnValue = _fullState.get(key);
        if (returnValue != null)
        {
            return returnValue;
        }
        ValueExpression expression = _component.getValueExpression(key
                .toString());
        if (expression != null)
        {
            return expression.getValue(_component.getFacesContext()
                    .getELContext());
        }
        return defaultValue;
    }

    public Object get(Serializable key)
    {
        return _fullState.get(key);
    }

    public Object put(Serializable key, Object value)
    {
        Object returnValue = null;
        if (_createDeltas())
        {
            if (_deltas.containsKey(key))
            {
                returnValue = _deltas.put(key, value);
                _fullState.put(key, value);
            }
            else
            {
                _deltas.put(key, value);
                returnValue = _fullState.put(key, value);
            }
        }
        else
        {
            /*
            if (value instanceof StateHolder)
            {
                _stateHolderKeys.add(key);
            }
            */
            returnValue = _fullState.put(key, value);
        }
        return returnValue;
    }

    public Object put(Serializable key, String mapKey, Object value)
    {
        boolean returnSet = false;
        Object returnValue = null;
        if (_createDeltas())
        {
            //Track delta case
            Map<String, Object> mapValues = (Map<String, Object>) _deltas
                    .get(key);
            if (mapValues == null)
            {
                mapValues = new InternalMap<String, Object>();
                _deltas.put(key, mapValues);
            }
            if (mapValues.containsKey(mapKey))
            {
                returnValue = mapValues.put(mapKey, value);
                returnSet = true;
            }
            else
            {
                mapValues.put(mapKey, value);
            }
        }

        //Handle change on full map
        Map<String, Object> mapValues = (Map<String, Object>) _fullState
                .get(key);
        if (mapValues == null)
        {
            mapValues = new InternalMap<String, Object>();
            _fullState.put(key, mapValues);
        }
        if (returnSet)
        {
            mapValues.put(mapKey, value);
        }
        else
        {
            returnValue = mapValues.put(mapKey, value);
        }
        return returnValue;
    }

    public Object remove(Serializable key)
    {
        Object returnValue = null;
        if (_createDeltas())
        {
            if (_deltas.containsKey(key))
            {
                // Keep track of the removed values using key/null pair on the delta map
                returnValue = _deltas.put(key, null);
                _fullState.remove(key);
            }
            else
            {
                // Keep track of the removed values using key/null pair on the delta map
                _deltas.put(key, null);
                returnValue = _fullState.remove(key);
            }
        }
        else
        {
            returnValue = _fullState.remove(key);
        }
        return returnValue;
    }

    public Object remove(Serializable key, Object valueOrKey)
    {
        // Comment by lu4242 : The spec javadoc says if it is a Collection 
        // or Map deal with it. But the intention of this method is work 
        // with add(?,?) and put(?,?,?), this ones return instances of 
        // InternalMap and InternalList to prevent mixing, so to be 
        // consistent we'll cast to those classes here.
        
        Object collectionOrMap = _fullState.get(key);
        Object returnValue = null;
        if (collectionOrMap instanceof InternalMap)
        {
            if (_createDeltas())
            {
                returnValue = _removeValueOrKeyFromMap(_deltas, key,
                        valueOrKey, true);
                _removeValueOrKeyFromMap(_fullState, key, valueOrKey, false);
            }
            else
            {
                returnValue = _removeValueOrKeyFromMap(_fullState, key,
                        valueOrKey, false);
            }
        }
        else if (collectionOrMap instanceof InternalList)
        {
            if (_createDeltas())
            {
                returnValue = _removeValueOrKeyFromCollectionDelta(_deltas,
                        key, valueOrKey);
                _removeValueOrKeyFromCollection(_fullState, key, valueOrKey);
            }
            else
            {
                returnValue = _removeValueOrKeyFromCollection(_fullState, key,
                        valueOrKey);
            }
        }
        return returnValue;
    }

    private static Object _removeValueOrKeyFromCollectionDelta(
            Map<Serializable, Object> stateMap, Serializable key,
            Object valueOrKey)
    {
        Object returnValue = null;
        Map<Object, Boolean> c = (Map<Object, Boolean>) stateMap.get(key);
        if (c != null)
        {
            if (c.containsKey(valueOrKey))
            {
                returnValue = valueOrKey;
            }
            c.put(valueOrKey, Boolean.FALSE);
        }
        return returnValue;
    }

    private static Object _removeValueOrKeyFromCollection(
            Map<Serializable, Object> stateMap, Serializable key,
            Object valueOrKey)
    {
        Object returnValue = null;
        Collection c = (Collection) stateMap.get(key);
        if (c != null)
        {
            if (c.remove(valueOrKey))
            {
                returnValue = valueOrKey;
            }
            if (c.isEmpty())
            {
                stateMap.remove(key);
            }
        }
        return returnValue;
    }

    private static Object _removeValueOrKeyFromMap(
            Map<Serializable, Object> stateMap, Serializable key,
            Object valueOrKey, boolean delta)
    {
        if (valueOrKey == null)
        {
            return null;
        }

        Object returnValue = null;
        Map<String, Object> map = (Map<String, Object>) stateMap.get(key);
        if (map != null)
        {
            if (delta)
            {
                // Keep track of the removed values using key/null pair on the delta map
                returnValue = map.put((String) valueOrKey, null);
            }
            else
            {
                returnValue = map.remove(valueOrKey);
            }

            if (map.isEmpty())
            {
                //stateMap.remove(key);
                stateMap.put(key, null);
            }
        }
        return returnValue;
    }

    public boolean isTransient()
    {
        return _transient;
    }

    /**
     * Serializing cod
     * the serialized data structure consists of key value pairs unless the value itself is an internal array
     * or a map in case of an internal array or map the value itself is another array with its initial value
     * myfaces.InternalArray, myfaces.internalMap
     *
     * the internal Array is then mapped to another array
     *
     * the internal Map again is then mapped to a map with key value pairs
     *
     *
     */
    public Object saveState(FacesContext context)
    {
        Map serializableMap = (isInitialStateMarked()) ? _deltas : _fullState;

        if (serializableMap == null || serializableMap.size() == 0)
        {
            return null;
        }
        
        /*
        int stateHolderKeyCount = 0;
        if (isInitalStateMarked())
        {
            for (Iterator<Serializable> it = _stateHolderKeys.iterator(); it.hasNext();)
            {
                Serializable key = it.next();
                if (!_deltas.containsKey(key))
                {
                    stateHolderKeyCount++;
                }
            }
        }*/
        
        Map.Entry<Serializable, Object> entry;
        //entry == key, value, key, value
        Object[] retArr = new Object[serializableMap.entrySet().size() * 2];
        //Object[] retArr = new Object[serializableMap.entrySet().size() * 2 + stateHolderKeyCount]; 

        Iterator<Map.Entry<Serializable, Object>> it = serializableMap
                .entrySet().iterator();
        int cnt = 0;
        while (it.hasNext())
        {
            entry = it.next();
            retArr[cnt] = entry.getKey();

            Object value = entry.getValue();
            
            // The condition in which the call to saveAttachedState
            // is to handle List, StateHolder or non Serializable instances.
            // we check it here, to prevent unnecessary calls.
            if (value instanceof StateHolder ||
                value instanceof List ||
                !(value instanceof Serializable))
            {
                Object savedValue = UIComponentBase.saveAttachedState(context,
                    value);
                retArr[cnt + 1] = savedValue;
            }
            else
            {
                retArr[cnt + 1] = value;
            }
            cnt += 2;
        }
        
        /*
        if (isInitalStateMarked())
        {
            for (Iterator<Serializable> it2 = _stateHolderKeys.iterator(); it.hasNext();)
            {
                Serializable key = it2.next();
                if (!_deltas.containsKey(key))
                {
                    retArr[cnt] = key;
                    Object value = _fullState.get(key);
                    if (value instanceof PartialStateHolder)
                    {
                        //Could contain delta, save it as _AttachedDeltaState
                        PartialStateHolder holder = (PartialStateHolder) value;
                        if (holder.isTransient())
                        {
                            retArr[cnt + 1] = null;
                        }
                        else
                        {
                            retArr[cnt + 1] = new _AttachedDeltaWrapper(value.getClass(), holder.saveState(context));
                        }
                    }
                    else
                    {
                        //Save everything
                        retArr[cnt + 1] = UIComponentBase.saveAttachedState(context, _fullState.get(key));
                    }
                    cnt += 2;
                }
            }
        }
        */       
        return retArr;
    }

    public void restoreState(FacesContext context, Object state)
    {
        if (state == null)
            return;

        Object[] serializedState = (Object[]) state;

        for (int cnt = 0; cnt < serializedState.length; cnt += 2)
        {
            Serializable key = (Serializable) serializedState[cnt];
            Object savedValue = UIComponentBase.restoreAttachedState(context,
                    serializedState[cnt + 1]);

            if (isInitialStateMarked())
            {
                if (savedValue instanceof InternalDeltaListMap)
                {
                    for (Map.Entry<Object, Boolean> mapEntry : ((Map<Object, Boolean>) savedValue)
                            .entrySet())
                    {
                        boolean addOrRemove = mapEntry.getValue();
                        if (addOrRemove)
                        {
                            //add
                            this.add(key, mapEntry.getKey());
                        }
                        else
                        {
                            //remove
                            this.remove(key, mapEntry.getKey());
                        }
                    }
                }
                else if (savedValue instanceof InternalMap)
                {
                    for (Map.Entry<String, Object> mapEntry : ((Map<String, Object>) savedValue)
                            .entrySet())
                    {
                        this.put(key, mapEntry.getKey(), mapEntry.getValue());
                    }
                }
                /*
                else if (savedValue instanceof _AttachedDeltaWrapper)
                {
                    _AttachedStateWrapper wrapper = (_AttachedStateWrapper) savedValue;
                    //Restore delta state
                    ((PartialStateHolder)_fullState.get(key)).restoreState(context, wrapper.getWrappedStateObject());
                    //Add this key as StateHolder key 
                    _stateHolderKeys.add(key);
                }
                */
                else
                {
                    put(key, savedValue);
                }
            }
            else
            {
                put(key, savedValue);
            }
        }
    }

    public void setTransient(boolean transientValue)
    {
        _transient = transientValue;
    }

    //We use our own data structures just to make sure
    //nothing gets mixed up internally
    static class InternalMap<K, V> extends HashMap<K, V> implements StateHolder
    {
        public InternalMap()
        {
            super();
        }

        public InternalMap(int initialCapacity, float loadFactor)
        {
            super(initialCapacity, loadFactor);
        }

        public InternalMap(Map<? extends K, ? extends V> m)
        {
            super(m);
        }

        public InternalMap(int initialSize)
        {
            super(initialSize);
        }

        public boolean isTransient()
        {
            return false;
        }

        public void setTransient(boolean newTransientValue)
        {
            // No op
        }

        public void restoreState(FacesContext context, Object state)
        {
            Object[] listAsMap = (Object[]) state;
            for (int cnt = 0; cnt < listAsMap.length; cnt += 2)
            {
                this.put((K) listAsMap[cnt], (V) UIComponentBase
                        .restoreAttachedState(context, listAsMap[cnt + 1]));
            }
        }

        public Object saveState(FacesContext context)
        {
            int cnt = 0;
            Object[] mapArr = new Object[this.size() * 2];
            for (Map.Entry<K, V> entry : this.entrySet())
            {
                mapArr[cnt] = entry.getKey();
                Object value = entry.getValue();
                
                if (value instanceof StateHolder ||
                    value instanceof List ||
                    !(value instanceof Serializable))
                {
                    mapArr[cnt + 1] = UIComponentBase.saveAttachedState(context, value);
                }
                else
                {
                    mapArr[cnt + 1] = value;
                }
                cnt += 2;
            }
            return mapArr;
        }
    }

    /**
     * Map used to keep track of list changes 
     */
    static class InternalDeltaListMap<K, V> extends InternalMap<K, V>
    {

        public InternalDeltaListMap()
        {
            super();
        }

        public InternalDeltaListMap(int initialCapacity, float loadFactor)
        {
            super(initialCapacity, loadFactor);
        }

        public InternalDeltaListMap(int initialSize)
        {
            super(initialSize);
        }

        public InternalDeltaListMap(Map<? extends K, ? extends V> m)
        {
            super(m);
        }
    }

    static class InternalList<T> extends ArrayList<T> implements StateHolder
    {
        public InternalList()
        {
            super();
        }

        public InternalList(Collection<? extends T> c)
        {
            super(c);
        }

        public InternalList(int initialSize)
        {
            super(initialSize);
        }

        public boolean isTransient()
        {
            return false;
        }

        public void setTransient(boolean newTransientValue)
        {
        }

        public void restoreState(FacesContext context, Object state)
        {
            Object[] listAsArr = (Object[]) state;
            //since all other options would mean dual iteration 
            //we have to do it the hard way
            for (Object elem : listAsArr)
            {
                add((T) UIComponentBase.restoreAttachedState(context, elem));
            }
        }

        public Object saveState(FacesContext context)
        {
            Object[] values = new Object[size()];
            for (int i = 0; i < size(); i++)
            {
                Object value = get(i);
                
                if (value instanceof StateHolder ||
                    value instanceof List ||
                    !(value instanceof Serializable))
                {
                    values[i] = UIComponentBase.saveAttachedState(context, value);
                }
                else
                {
                    values[i] = value;
                }                
            }
            return values;
        }
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.