myfaces-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From we...@apache.org
Subject svn commit: r811408 - in /myfaces/core/trunk: api/src/main/java/javax/faces/component/behavior/ impl/src/main/java/org/apache/myfaces/renderkit/html/
Date Fri, 04 Sep 2009 13:51:18 GMT
Author: werpu
Date: Fri Sep  4 13:51:17 2009
New Revision: 811408

URL: http://svn.apache.org/viewvc?rev=811408&view=rev
Log:
https://issues.apache.org/jira/browse/MYFACES-2347

Done, implemented both the renderer and the Delta state saving

Added:
    myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/_AjaxBehaviorDeltaStateHelper.java
    myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
Modified:
    myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/AjaxBehavior.java

Modified: myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/AjaxBehavior.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/AjaxBehavior.java?rev=811408&r1=811407&r2=811408&view=diff
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/AjaxBehavior.java
(original)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/AjaxBehavior.java
Fri Sep  4 13:51:17 2009
@@ -18,11 +18,7 @@
  */
 package javax.faces.component.behavior;
 
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 
 import javax.el.ValueExpression;
 import javax.faces.context.FacesContext;
@@ -31,196 +27,245 @@
 /**
  * @author Simon Lessard (latest modification by $Author: slessard $)
  * @version $Revision: 696523 $ $Date: 2009-03-14 16:04:27 -0400 (mer., 17 sept. 2008) $
- * 
  * @since 2.0
  */
-public class AjaxBehavior extends ClientBehaviorBase
-{
+public class AjaxBehavior extends ClientBehaviorBase {
+
+    /**
+     * not needed anymore but enforced by the spec
+     * theoretically a
+     * @FacesBehavior(value = "javax.faces.behavior.Ajax")
+     * could do it
+     */
     public static final String BEHAVIOR_ID = "javax.faces.behavior.Ajax";
-    
-    private static final Set<ClientBehaviorHint> HINTS = Collections.singleton(ClientBehaviorHint.SUBMITTING);
 
-    private static final String ATTR_DISABLED = "disabled";
+    //To enable delta state saving we need this one
+    _AjaxBehaviorDeltaStateHelper<AjaxBehavior> deltaStateHelper = new _AjaxBehaviorDeltaStateHelper<AjaxBehavior>(this);
     private static final String ATTR_EXECUTE = "execute";
-    private static final String ATTR_IMMEDIATE = "immediate";
-    private static final String ATTR_ON_ERROR = "onError";
-    private static final String ATTR_ON_EVENT = "onEvent";
+    private static final String ATTR_ON_ERROR = "onerror";
+    private static final String ATTR_ON_EVENT = "onevent";
     private static final String ATTR_RENDER = "render";
-    
-    private Boolean _disabled;
-    private Collection<String> _execute;
-    private Boolean _immediate;
-    private String _onError;
-    private String _onEvent;
-    private Collection<String> _render;
-    
-    private Map<String, ValueExpression> _expressions;
-    
+    private static final String ATTR_DISABLED = "disabled";
+    private static final String ATTR_IMMEDIATE = "immediate";
+
+
+    /**
+     * special render and execute targets
+     */
+    private static final String VAL_FORM = "@form";
+    private static final String VAL_ALL = "@all";
+    private static final String VAL_THIS = "@this";
+    private static final String VAL_NONE = "@none";
+
+    //we cannot use Collection - singletons here otherwise the delta state restore would
fail
+    private static final Collection<String> VAL_FORM_LIST = new ArrayList<String>(1);
+    private static final Collection<String> VAL_ALL_LIST = new ArrayList<String>(1);
+    private static final Collection<String> VAL_THIS_LIST = new ArrayList<String>(1);
+    private static final Collection<String> VAL_NONE_LIST = new ArrayList<String>(1);
 
     /**
-     * 
+     * internal errors
      */
-    public AjaxBehavior()
-    {
-        // Default capacity (10) should be decent with the default load factor (0.75)
-        // So there's a total of 7 slots in the map with 7 default attributes
-        _expressions = new HashMap<String, ValueExpression>();
+    private static final String ERR_KEY_IS_NULL = "Given value expression key is null";
+
+
+    Map<String, ValueExpression> _valueExpressions = new HashMap<String, ValueExpression>();
+
+    static {
+        VAL_FORM_LIST.add(VAL_FORM);
+        VAL_ALL_LIST.add(VAL_ALL);
+        VAL_THIS_LIST.add(VAL_THIS);
+        VAL_NONE_LIST.add(VAL_NONE);
+
     }
-    
-    public void addAjaxBehaviorListener(AjaxBehaviorListener listener)
-    {
-        addBehaviorListener(listener);
+
+
+    public AjaxBehavior() {
+        super();
+        getValueExpressionMap();
     }
-    
-    public Collection<String> getExecute()
-    {
-        return _getIds(ATTR_EXECUTE, _execute);
+
+
+    private Map<String, ValueExpression> getValueExpressionMap() {
+        return _valueExpressions;
     }
-    
-    public Set<ClientBehaviorHint> getHints()
-    {
-        // FIXME: Useless, notify the EG... Mojarra returns a static Set containing SUBMITTING...
-        // Copying Mojarra behavior for now by assuming they simply forgot to add JavaDoc
in the class
-        return HINTS;
+
+
+    public void addAjaxBehaviorListener(AjaxBehaviorListener listener) {
+        super.addBehaviorListener(listener);
     }
-    
-    public String getOnerror()
-    {
-        return _resolve(ATTR_ON_ERROR, _onError);
+
+    public Collection<String> getExecute() {
+        Object execute = deltaStateHelper.eval(ATTR_EXECUTE);
+
+        return (Collection<String>) execute;
     }
-    
-    public String getOnevent()
-    {
-        return _resolve(ATTR_ON_EVENT, _onEvent);
+
+
+    public void setExecute(Collection<String> execute) {
+        deltaStateHelper.put(ATTR_EXECUTE, execute);
     }
-    
-    public Collection<String> getRender()
-    {
-        return _getIds(ATTR_RENDER, _render);
+
+    public String getOnerror() {
+        return (String) deltaStateHelper.eval(ATTR_ON_ERROR);
     }
 
-    public String getRendererType()
-    {
-        return BEHAVIOR_ID;
+    public void setOnerror(String onError) {
+        deltaStateHelper.put(ATTR_ON_ERROR, onError);
     }
-    
-    public ValueExpression getValueExpression(String name)
-    {
-        _checkNull(name);
-        
-        return _expressions.get(name);
+
+    public String getOnevent() {
+        return (String) deltaStateHelper.eval(ATTR_ON_EVENT);
     }
-    
-    public boolean isDisabled()
-    {
-        return Boolean.TRUE.equals(_resolve(ATTR_DISABLED, _disabled));
+
+    public void setOnevent(String onEvent) {
+        deltaStateHelper.put(ATTR_ON_EVENT, onEvent);
     }
-    
-    public boolean isImmediate()
-    {
-        return Boolean.TRUE.equals(_resolve(ATTR_IMMEDIATE, _immediate));
+
+    public Collection<String> getRender() {
+        Object render = deltaStateHelper.eval(ATTR_RENDER);
+        if (render instanceof String) {
+            render = ((String) render).split(" ");
+            render = Arrays.asList((String[]) render);
+        }
+
+        return (Collection<String>) render;
     }
-    
-    public boolean isImmediateSet()
-    {
-        return _immediate != null || _expressions.containsKey(ATTR_IMMEDIATE);
+
+    public void setRender(Collection<String> render) {
+        deltaStateHelper.put(ATTR_RENDER, render);
     }
-    
-    public void removeAjaxBehaviorListener(AjaxBehaviorListener listener)
-    {
-        removeBehaviorListener(listener);
+
+    public ValueExpression getValueExpression(String name) {
+        return getValueExpressionMap().get(name);
+    }
+
+    public void setValueExpression(String name, ValueExpression item) {
+        if (item == null) {
+            getValueExpressionMap().remove(name);
+            deltaStateHelper.remove(name);
+        } else {
+            Object value = item.getValue(FacesContext.getCurrentInstance().getELContext());
+            //the tag handler sets the values over value expressions we do have to do a conversion
+            if (name.equals(ATTR_EXECUTE) && value instanceof String) {
+                //TODO please move this conversion code over to the tag handler
+                //I do not think it belongs in here
+                applyArrayAttributeFromString(ATTR_EXECUTE, value);
+            } else if (name.equals(ATTR_RENDER) && value instanceof String) {
+                applyArrayAttributeFromString(ATTR_RENDER, value);
+            }
+            getValueExpressionMap().put(name, item);
+        }
     }
-    
-    @Override
-    public void restoreState(FacesContext context, Object state)
-    {
-        // TODO: IMPLEMENT HERE - Full + delta support
-        super.restoreState(context, state);
+
+    /**
+     * facelets applies its values over the value expressions
+     * to have our system working fully we need to reapply
+     * the values to our delta state saver
+     * in converted form so that getters and setters can work on the maps instead
+     * of the string value expression, the delta states have higher priority
+     * than the value expressions
+     * Since Behaviors do not have converters we have to do it here (the getter
+     * or setter is a no go due to the call frequency of this method)
+     *
+     * @param attribute the attribute name
+     * @param value     the value expression to be changed to a collection
+     */
+    private void applyArrayAttributeFromString(String attribute, Object value) {
+
+        String stringValue = (String) value;
+        //@special handling for @all, @none, @form and @this
+        if (stringValue.equals(VAL_FORM)) {
+            deltaStateHelper.put(attribute, VAL_FORM_LIST);
+            return;
+        } else if (stringValue.equals(VAL_ALL)) {
+            deltaStateHelper.put(attribute, VAL_ALL_LIST);
+            return;
+        } else if (stringValue.equals(VAL_NONE)) {
+            deltaStateHelper.put(attribute, VAL_NONE_LIST);
+            return;
+        } else if (stringValue.equals(VAL_THIS)) {
+            deltaStateHelper.put(attribute, VAL_THIS_LIST);
+            return;
+        }
+
+        String[] arrValue = ((String) stringValue).split(",");
+
+        //we have to manually convert otherwise the delta state saving fails
+        //Arrays.asList returns a list which is not instantiable
+
+        List<String> saveVal = new ArrayList<String>(arrValue.length);
+        saveVal.addAll(Arrays.asList(arrValue));
+
+        deltaStateHelper.put(attribute, saveVal);
     }
 
-    @Override
-    public Object saveState(FacesContext context)
-    {
-        // TODO: IMPLEMENT HERE - Full + delta support
-        return super.saveState(context);
+
+    public boolean isDisabled() {
+        Boolean retVal = (Boolean) deltaStateHelper.eval(ATTR_DISABLED);
+        retVal = (retVal == null) ? false : retVal;
+        return retVal;
     }
-    
-    public void setDisabled(boolean disabled)
-    {
-        this._disabled = disabled;
+
+    public void setDisabled(boolean disabled) {
+        deltaStateHelper.put(ATTR_DISABLED, disabled);
     }
 
-    public void setExecute(Collection<String> execute)
-    {
-        this._execute = execute;
+    public boolean isImmediate() {
+        Boolean retVal = (Boolean) deltaStateHelper.eval(ATTR_IMMEDIATE);
+        retVal = (retVal == null) ? false : retVal;
+        return retVal;
     }
 
-    public void setImmediate(boolean immediate)
-    {
-        this._immediate = immediate;
+    public void setImmediate(Boolean immediate) {
+        deltaStateHelper.put(ATTR_IMMEDIATE, immediate);
     }
 
-    public void setOnerror(String error)
-    {
-        _onError = error;
+    public boolean isImmediateSet() {
+        return deltaStateHelper.eval(ATTR_IMMEDIATE) != null;
     }
 
-    public void setOnevent(String event)
-    {
-        _onEvent = event;
+
+    public void removeAjaxBehaviorListener(AjaxBehaviorListener listener) {
+        removeBehaviorListener(listener);
     }
 
-    public void setRender(Collection<String> render)
-    {
-        this._render = render;
+
+    @Override
+    public Set<ClientBehaviorHint> getHints() {
+        return EnumSet.of(ClientBehaviorHint.SUBMITTING);
     }
-    
-    public void setValueExpression(String name, ValueExpression expression)
-    {
-        _checkNull(name);
-        
-        if (expression == null)
-        {
-            _expressions.remove(name);
-        }
-        else
-        {
-            _expressions.put(name, expression);
-        }
+
+
+    @Override
+    public String getRendererType() {
+        return BEHAVIOR_ID;
     }
 
-    private void _checkNull(String name)
-    {
-        if (name == null)
-        {
-            throw new NullPointerException();
+
+    @Override
+    public void restoreState(FacesContext facesContext, Object o) {
+        Object[] values = (Object[]) o;
+
+        if (values[0] != null) {
+            super.restoreState(facesContext, values[0]);
         }
+       deltaStateHelper.restoreState(facesContext, values[1]);
     }
 
-    private Collection<String> _getIds(String name, Collection<String> explicitValue)
-    {
-        // FIXME: I don't see how I can make it non empty when there's nothing that I can
put in it
-        //        by default
-        Collection<String> ids = _resolve(name, explicitValue);
-        
-        return ids == null ? Collections.<String>emptyList() : ids;
+    @Override
+    public Object saveState(FacesContext facesContext) {
+        Object[] values = new Object[2];
+        values[0] = super.saveState(facesContext);
+        values[1] = deltaStateHelper.saveState(facesContext);
+        return values;
     }
-    
-    @SuppressWarnings("unchecked")
-    private <T> T _resolve(String name, T explicitValue)
-    {
-        assert name != null;
-        assert name.length() > 0;
-        
-        if (explicitValue == null)
-        {
-            ValueExpression expression = _expressions.get(name);
-            if (expression != null)
-            {
-                explicitValue = (T)expression.getValue(FacesContext.getCurrentInstance().getELContext());
-            }
+
+    private void assertNull(Object item) {
+        if (item == null) {
+            throw new NullPointerException(ERR_KEY_IS_NULL);
         }
-        
-        return explicitValue;
     }
+    
+
 }

Added: myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/_AjaxBehaviorDeltaStateHelper.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/_AjaxBehaviorDeltaStateHelper.java?rev=811408&view=auto
==============================================================================
--- myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/_AjaxBehaviorDeltaStateHelper.java
(added)
+++ myfaces/core/trunk/api/src/main/java/javax/faces/component/behavior/_AjaxBehaviorDeltaStateHelper.java
Fri Sep  4 13:51:17 2009
@@ -0,0 +1,589 @@
+/*
+ * 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.behavior;
+
+import javax.faces.component.StateHelper;
+import javax.faces.component.StateHolder;
+import javax.faces.component.UIComponentBase;
+import javax.faces.component.PartialStateHolder;
+import javax.faces.context.FacesContext;
+import javax.el.ValueExpression;
+import java.io.Serializable;
+import java.util.*;
+
+ /**
+ * @author Werner Punz (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ *
+ * Delta state helper to deal with the ajax
+ * behavior delta states, cross ported
+ * from our generic delta state, due to limitations
+ * in the spec (sorry for the somewhat dirty code here maybe
+  * we can unify both implementations by isolating a common protected interface)
+ *
+ * @since 2.0
+ */
+class _AjaxBehaviorDeltaStateHelper <A extends AjaxBehavior> implements StateHelper
{
+      /**
+     * We need to hold a component instance because:
+     * <p/>
+     * - The component is the one who knows if we are on initial or delta mode
+     * - eval assume calls to component.ValueExpression
+     */
+    private A _target;
+
+    /**
+     * 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 _AjaxBehaviorDeltaStateHelper(A component) {
+        super();
+        this._target = 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 _target.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 = _target.getValueExpression(key
+                .toString());
+        if (expression != null) {
+            return expression.getValue(FacesContext.getCurrentInstance()
+                    .getELContext());
+        }
+        return null;
+    }
+
+    public Object eval(Serializable key, Object defaultValue) {
+        Object returnValue = _fullState.get(key);
+        if (returnValue != null) {
+            return returnValue;
+        }
+        ValueExpression expression = _target.getValueExpression(key
+                .toString());
+        if (expression != null) {
+            return expression.getValue(FacesContext.getCurrentInstance()
+                    .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
+     * <p/>
+     * the internal Array is then mapped to another array
+     * <p/>
+     * 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;
+        }
+    }
+}

Added: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java?rev=811408&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
(added)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/renderkit/html/HtmlAjaxBehaviorRenderer.java
Fri Sep  4 13:51:17 2009
@@ -0,0 +1,278 @@
+/*
+ * 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 org.apache.myfaces.renderkit.html;
+
+import javax.faces.context.FacesContext;
+import javax.faces.component.UIComponent;
+import javax.faces.component.behavior.ClientBehavior;
+import javax.faces.component.behavior.ClientBehaviorContext;
+import javax.faces.component.behavior.AjaxBehavior;
+import javax.faces.event.PhaseId;
+import javax.faces.event.AjaxBehaviorEvent;
+import javax.faces.FacesException;
+import javax.faces.render.FacesBehaviorRenderer;
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+
+/**
+ * @author Werner Punz  (latest modification by $Author$)
+ * @version $Revision$ $Date$
+ */
+@FacesBehaviorRenderer(rendererType = "javax.faces.behavior.Ajax", renderKitId = "HTML_BASIC")
+public class HtmlAjaxBehaviorRenderer {
+
+    private static final String QUOTE = "'";
+    private static final String BLANK = " ";
+
+    private static final String AJAX_KEY_ONERROR = "onerror";
+    private static final String AJAX_KEY_ONEVENT = "onevent";
+    private static final String AJAX_KEY_EXECUTE = "execute";
+    private static final String AJAX_KEY_RENDER = "render";
+
+    private static final String AJAX_VAL_THIS = "this";
+    private static final String AJAX_VAL_EVENT = "event";
+    private static final String JS_AJAX_REQUEST = "jsf.ajax.request";
+
+    private static final String COLON = ":";
+    private static final String EMPTY = "";
+    private static final String COMMA = ",";
+
+    private static final String ERR_NO_AJAX_BEHAVIOR = "The behavior must be an instance
of AjaxBehavior";
+    private static final String L_PAREN = "(";
+    private static final String R_PAREN = ")";
+
+    /*if this marker is present in the request we have to dispatch a behavior event*/
+    /*if an attached behavior triggers an ajax request this request param must be added*/
+    private static final String BEHAVIOR_EVENT = "javax.faces.behavior.event";
+    private static final String IDENTIFYER_MARKER = "@";
+
+    public void decode(FacesContext context, UIComponent component,
+                       ClientBehavior behavior) {
+
+        AjaxBehavior ajaxBehavior = (AjaxBehavior) behavior;
+        assertBehavior(behavior);
+        if (ajaxBehavior.isDisabled() || !component.isRendered()) {
+            return;
+        }
+
+        dispatchBehaviorEvent(component, ajaxBehavior);
+    }
+
+
+    public String getScript(ClientBehaviorContext behaviorContext,
+                            ClientBehavior behavior) {
+        return makeAjax(behaviorContext, (AjaxBehavior) behavior).toString();
+    }
+
+
+    private final void dispatchBehaviorEvent(UIComponent component, AjaxBehavior ajaxBehavior)
{
+        AjaxBehaviorEvent event = new AjaxBehaviorEvent(component, ajaxBehavior);
+
+        PhaseId phaseId = ajaxBehavior.isImmediate() || isComponentImmediate(component) ?
+                          PhaseId.APPLY_REQUEST_VALUES :
+                          PhaseId.INVOKE_APPLICATION;
+
+        event.setPhaseId(phaseId);
+
+        component.queueEvent(event);
+    }
+
+
+    private final boolean isComponentImmediate(UIComponent component) {
+        /**
+         * Currently implemented by ActionSource and EditableValueHolder
+         * but we cannot be sure about both interfaces so
+         * lets make introspection calls here
+         */
+        Method immediate = null;
+        try {
+            immediate = component.getClass().getMethod("isImmediate", new Class[]{});
+            //public isImmediate must be present
+            if (Modifier.isPublic(immediate.getModifiers()) ||
+                immediate.getReturnType().equals(boolean.class) ||
+                immediate.getReturnType().equals(Boolean.class)) /*autoboxing*/ {
+                return (Boolean) immediate.invoke(component, new Object[]{});
+            }
+
+            return false;
+        } catch (NoSuchMethodException e) {
+            //not implemented at all we can return, this is
+            //not really a programmatic exception but we do not have an
+            //hasMethod, and iterating over all methods is way slower
+            return false;
+        } catch (InvocationTargetException e) {
+            throw new FacesException(e);
+        } catch (IllegalAccessException e) {
+            throw new FacesException(e);
+        }
+    }
+
+
+    /**
+     * builds the generic ajax call depending upon
+     * the ajax behavior parameters
+     *
+     * @param context  the Client behavior context
+     * @param behavior the behavior
+     * @return a fully working javascript with calls into jsf.js
+     */
+    private final StringBuilder makeAjax(ClientBehaviorContext context, AjaxBehavior behavior)
{
+
+        StringBuilder retVal = new StringBuilder();
+
+        StringBuilder executes = mapToString(context, AJAX_KEY_EXECUTE, behavior.getExecute());
+        StringBuilder render = mapToString(context, AJAX_KEY_RENDER, behavior.getRender());
+
+        String onError = behavior.getOnerror();
+        onError = (onError != null && onError.trim().equals(EMPTY)) ? AJAX_KEY_ONERROR
+ COLON + onError : null;
+        String onEvent = behavior.getOnevent();
+        onEvent = (onEvent != null && onEvent.trim().equals(EMPTY)) ? AJAX_KEY_ONEVENT
+ COLON + onEvent : null;
+
+        String sourceId = (context.getSourceId() == null) ? AJAX_VAL_THIS : context.getSourceId();
+        String event = context.getEventName();
+
+        retVal.append(JS_AJAX_REQUEST);
+        retVal.append(L_PAREN);
+        retVal.append(sourceId);
+        retVal.append(COMMA);
+        retVal.append(AJAX_VAL_EVENT);
+        retVal.append(COMMA);
+
+        Collection<ClientBehaviorContext.Parameter> params = context.getParameters();
+        int paramSize = (params != null) ? params.size() : 0;
+
+        List<String> parameterList = new ArrayList(paramSize + 2);
+        if (executes != null) {
+            parameterList.add(executes.toString());
+        }
+        if (render != null) {
+            parameterList.add(render.toString());
+        }
+        if (paramSize > 0) {
+            /**
+             * see ClientBehaviorContext.html of the spec
+             * the param list has to be added in the post back
+             */
+            for (ClientBehaviorContext.Parameter param : params) {
+                //TODO we may need a proper type handling in this part
+                //lets leave it for now as it is
+                //quotes etc.. should be transferred directly
+                //and the rest is up to the toString properly implemented
+                parameterList.add(param.getName() + COLON + param.getValue().toString());
+            }
+        }
+
+        parameterList.add(QUOTE + BEHAVIOR_EVENT + QUOTE + COLON + QUOTE + event + QUOTE);
+
+        /**
+         * I assume here for now that the options are the same which also
+         * can be sent via the options attribute to javax.faces.ajax
+         * this still needs further clarifications but I assume so for now
+         */
+        retVal.append(buildOptions(parameterList));
+
+        retVal.append(R_PAREN);
+
+        return retVal;
+    }
+
+
+    private StringBuilder buildOptions(List<String> options) {
+        StringBuilder retVal = new StringBuilder();
+        retVal.append("{");
+
+        boolean first = true;
+        for (String option : options) {
+            if (option != null && !option.trim().equals(EMPTY)) {
+                if (!first) {
+                    retVal.append(COMMA);
+                } else {
+                    first = false;
+                }
+                retVal.append(option);
+            }
+        }
+        retVal.append("}");
+        return retVal;
+    }
+
+    private final StringBuilder mapToString(ClientBehaviorContext context, String target,
Collection<String> dataHolder) {
+        StringBuilder retVal = new StringBuilder(20);
+
+        if (dataHolder == null) {
+            dataHolder = Collections.EMPTY_LIST;
+        }
+        int executeSize = dataHolder.size();
+        if (executeSize > 0) {
+
+            retVal.append(target);
+            retVal.append(COLON);
+            retVal.append(QUOTE);
+
+            int cnt = 0;
+            for (String strVal : dataHolder) {
+                cnt++;
+                strVal = strVal.trim();
+                if (!strVal.equals("")) {
+                    if (!strVal.startsWith(IDENTIFYER_MARKER)) {
+                        retVal.append(getComponentId(context, strVal));
+                    } else {
+
+                        retVal.append(strVal);
+                        if (cnt < dataHolder.size()) {
+                            retVal.append(BLANK);
+                        }
+
+                    }
+                }
+            }
+
+            retVal.append(QUOTE);
+            return retVal;
+        }
+        return null;
+
+    }
+
+
+    private final String getComponentId(ClientBehaviorContext context, String id) {
+
+        UIComponent contextComponent = context.getComponent();
+        UIComponent target = contextComponent.findComponent(id);
+        if (target == null) {
+            target = contextComponent.findComponent(COLON + id);
+        }
+        if (target != null) {
+            return target.getClientId();
+        }
+        throw new FacesException("Component with id:" + id + " not found");
+    }
+
+    private final void assertBehavior(ClientBehavior behavior) {
+        if (!(behavior instanceof AjaxBehavior)) {
+            throw new FacesException(ERR_NO_AJAX_BEHAVIOR);
+        }
+    }
+
+}



Mime
View raw message