Commits

Anonymous committed 313fa06

add copy of accessors that do not depend on ognl

git-svn-id: http://svn.opensymphony.com/svn/xwork/branches/parameter-binder@2009e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (10)

core/src/main/java/com/opensymphony/xwork2/config/providers/XWorkConfigurationProvider.java

 import com.opensymphony.xwork2.TextProvider;
 import com.opensymphony.xwork2.UnknownHandlerManager;
 import com.opensymphony.xwork2.parameters.XWorkParametersBinder;
+import com.opensymphony.xwork2.parameters.accessor.*;
 import com.opensymphony.xwork2.config.Configuration;
 import com.opensymphony.xwork2.config.ConfigurationException;
 import com.opensymphony.xwork2.config.ConfigurationProvider;
                .factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
                .factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
                .factory(XWorkConverter.class, Scope.SINGLETON)
-               .factory(XWorkParametersBinder.class, Scope.SINGLETON) 
+               .factory(XWorkParametersBinder.class, Scope.SINGLETON)
                .factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
                .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
                .factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
                .factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
                .factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
                .factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
+
+               .factory(ParametersPropertyAccessor.class, List.class.getName(), ParametersListPropertyAccessor.class, Scope.SINGLETON)
+               .factory(ParametersPropertyAccessor.class, ArrayList.class.getName(), ParametersListPropertyAccessor.class, Scope.SINGLETON)
+               .factory(ParametersPropertyAccessor.class, HashSet.class.getName(), ParametersCollectionPropertyAccessor.class, Scope.SINGLETON)
+               .factory(ParametersPropertyAccessor.class, Set.class.getName(), ParametersCollectionPropertyAccessor.class, Scope.SINGLETON)
+               .factory(ParametersPropertyAccessor.class, HashMap.class.getName(), ParametersMapPropertyAccessor.class, Scope.SINGLETON)
+               .factory(ParametersPropertyAccessor.class, Map.class.getName(), ParametersMapPropertyAccessor.class, Scope.SINGLETON)
+               .factory(ParametersPropertyAccessor.class, CompoundRoot.class.getName(), ParametersCompoundRootAccessor.class, Scope.SINGLETON)
+               .factory(ParametersPropertyAccessor.class, Object.class.getName(), ParametersObjectPropertyAccessor.class, Scope.SINGLETON)
                
                // silly workarounds for ognl since there is no way to flush its caches
                .factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)

core/src/main/java/com/opensymphony/xwork2/parameters/XWorkParametersBinder.java

 import com.opensymphony.xwork2.parameters.nodes.IdentifierNode;
 import com.opensymphony.xwork2.parameters.nodes.IndexedNode;
 import com.opensymphony.xwork2.parameters.nodes.CollectionNode;
+import com.opensymphony.xwork2.parameters.accessor.ParametersPropertyAccessor;
 import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
 import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
 import com.opensymphony.xwork2.util.CompoundRoot;
 import com.opensymphony.xwork2.conversion.NullHandler;
-import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
 
 import java.util.*;
 
 import org.apache.commons.lang.StringUtils;
-import ognl.PropertyAccessor;
-import ognl.OgnlException;
-import ognl.OgnlContext;
+
 
 public class XWorkParametersBinder {
     protected ReflectionProvider reflectionProvider;
     protected NullHandler nullHandler;
     protected Container container;
-    private XWorkParametersMapPropertyAccessor mapAccessor;
+
+    protected ParametersPropertyAccessor mapAccessor;
+    protected ParametersPropertyAccessor compoundAccessor;
+    protected ParametersPropertyAccessor objectAccessor;
+    protected ParametersPropertyAccessor listAccessor;
+    protected ParametersPropertyAccessor collectionAccesor;
 
     private static final Map<String, List<Node>> nodesCache = new WeakHashMap<String, List<Node>>();
 
-    public void setProperty(Map<String, Object> context, Object action, String paramName, Object paramValue) {
+    public void setProperty(Map<String, Object> originalContext, Object action, String paramName, Object paramValue) {
         try {
-            OgnlContext ognlContext = new OgnlContext(context);
+            Map context = getImplemenationContext(originalContext);
 
             List<Node> nodes = nodesCache.get(paramName);
             if (nodes == null) {
                     lastProperty = id;
 
                     //if this is not the last expression, create the object if it doesn't exist
-                    PropertyAccessor accessor = getPropertyAccessor(lastObject);
-                    Object value = accessor.getProperty(ognlContext, lastObject, id);
+                    ParametersPropertyAccessor accessor = getPropertyAccessor(lastObject);
+                    Object value = accessor.getProperty(context, lastObject, id);
                     if (!lastNode) {
                         if (value == null) {
                             //create it
-                            value = create(ognlContext, action, id);
+                            value = create(context, action, id);
                         }
                         lastObject = value;
                     }
                     Object index = indexedNode.getIndex();
 
                     lastProperty = index;
-                    PropertyAccessor accessor = getPropertyAccessor(lastObject);
+                    ParametersPropertyAccessor accessor = getPropertyAccessor(lastObject);
                     //the list or map
-                    Object container = accessor.getProperty(ognlContext, lastObject, id);
+                    Object container = accessor.getProperty(context, lastObject, id);
 
                     if (container == null) {
                         //create the list or map
-                        container = create(ognlContext, lastObject, id);
+                        container = create(context, lastObject, id);
                     }
 
                     lastObject = container;
                     if (!lastNode) {
                         //the expression goes on like A[B].C, so now create A[B]
                         accessor = getPropertyAccessor(lastObject);
-                        lastObject = getAndCreate(ognlContext, lastObject, index, accessor);
+                        lastObject = getAndCreate(context, lastObject, index, accessor);
                     }
                 } else if (node instanceof CollectionNode) {
                     //A(B)
                     Object index = indexedNode.getIndex();
 
                     lastProperty = index;
-                    PropertyAccessor accessor = getPropertyAccessor(lastObject);
-                    lastObject = accessor.getProperty(ognlContext, lastObject, id);
+                    ParametersPropertyAccessor accessor = getPropertyAccessor(lastObject);
+                    lastObject = accessor.getProperty(context, lastObject, id);
 
                     //create the lastObject
                     if (lastObject == null) {
                         //create it
-                        lastObject = create(ognlContext, action, id);
+                        lastObject = create(context, action, id);
                     }
                 }
             }
 
-            PropertyAccessor accessor = getPropertyAccessor(lastObject);
-            accessor.setProperty(ognlContext, lastObject, lastProperty, paramValue);
+            ParametersPropertyAccessor accessor = getPropertyAccessor(lastObject);
+            accessor.setProperty(context, lastObject, lastProperty, paramValue);
         } catch (Throwable e) {
             throw new RuntimeException(e);
         }
     }
 
-    protected PropertyAccessor getPropertyAccessor(Object object) {
+    protected Map getImplemenationContext(Map context) {
+        return context;
+    }
+
+    protected ParametersPropertyAccessor getPropertyAccessor(Object object) {
         if (object instanceof CompoundRoot)
-            return container.getInstance(PropertyAccessor.class, CompoundRoot.class.getName());
+            return compoundAccessor;
         if (object instanceof Map)
             return mapAccessor;
         else if (object instanceof List)
-            return container.getInstance(PropertyAccessor.class, List.class.getName());
+            return listAccessor;
         else if (object instanceof Collection)
-            return container.getInstance(PropertyAccessor.class, Collection.class.getName());
+            return collectionAccesor;
         else if (object instanceof Enumeration)
-            return container.getInstance(PropertyAccessor.class, Enumeration.class.getName());
+            return container.getInstance(ParametersPropertyAccessor.class, Enumeration.class.getName());
         else if (object instanceof Iterator)
-            return container.getInstance(PropertyAccessor.class, Iterator.class.getName());
+            return container.getInstance(ParametersPropertyAccessor.class, Iterator.class.getName());
         else
-            return container.getInstance(PropertyAccessor.class, Object.class.getName());
+            return objectAccessor;
 
     }
 
         }
     }
 
-    protected Object getAndCreate(Map<String, Object> context, Object root, Object property, PropertyAccessor accessor) throws OgnlException {
+    protected Object getAndCreate(Map<String, Object> context, Object root, Object property, ParametersPropertyAccessor accessor) throws Exception {
         boolean originalValue = ReflectionContextState.isCreatingNullObjects(context);
         try {
             ReflectionContextState.setCreatingNullObjects(context, true);
-            return accessor.getProperty(context, root, property);
+            return accessor.getProperty(context, root, property.toString());
         } finally {
             ReflectionContextState.setCreatingNullObjects(context, originalValue);
         }
     @Inject
     public void setContainer(Container container) {
         this.container = container;
-        this.mapAccessor = new XWorkParametersMapPropertyAccessor();
-        container.inject(mapAccessor);
+    }
+
+    @Inject("java.util.Map")
+    public void setMapAccessor(ParametersPropertyAccessor mapAccessor) {
+        this.mapAccessor = mapAccessor;
+    }
+
+    @Inject("com.opensymphony.xwork2.util.CompoundRoot")
+    public void setCompoundAccessor(ParametersPropertyAccessor compoundAccessor) {
+        this.compoundAccessor = compoundAccessor;
+    }
+
+    @Inject("java.lang.Object")
+    public void setObjectAccessor(ParametersPropertyAccessor objectAccessor) {
+        this.objectAccessor = objectAccessor;
+    }
+
+    @Inject("java.util.List")
+    public void setListAccessor(ParametersPropertyAccessor listAccessor) {
+        this.listAccessor = listAccessor;
+    }
+
+    @Inject("java.util.Set")
+    public void setCollectionAccesor(ParametersPropertyAccessor collectionAccesor) {
+        this.collectionAccesor = collectionAccesor;
     }
 }

core/src/main/java/com/opensymphony/xwork2/parameters/XWorkSimpleMapPropertyAccessor.java

-/*
- * $Id$
- *
- * 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 com.opensymphony.xwork2.parameters;
-
-import ognl.MapPropertyAccessor;
-import ognl.OgnlException;
-import com.opensymphony.xwork2.util.logging.Logger;
-import com.opensymphony.xwork2.util.logging.LoggerFactory;
-import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
-import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
-import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
-import com.opensymphony.xwork2.ObjectFactory;
-import com.opensymphony.xwork2.ognl.accessor.XWorkMapPropertyAccessor;
-import com.opensymphony.xwork2.inject.Inject;
-
-import java.util.Map;
-
-/**
- * Same code as XWorkMapPropertyAccessor, but all exceptions are catched from
- * result = super.getProperty(context, target, name);
- * because we didn't parse the expression as an ONGL expression, calling getProperty might fail in some
- * scenarios. This class is not intended to be used outside this package
- */
-class XWorkParametersMapPropertyAccessor extends MapPropertyAccessor {
-
-    private static final Logger LOG = LoggerFactory.getLogger(XWorkMapPropertyAccessor.class);
-
-    private static final String[] INDEX_ACCESS_PROPS = new String[]
-            {"size", "isEmpty", "keys", "values"};
-
-    private XWorkConverter xworkConverter;
-    private ObjectFactory objectFactory;
-    private ObjectTypeDeterminer objectTypeDeterminer;
-
-    @Inject
-    public void setXWorkConverter(XWorkConverter conv) {
-        this.xworkConverter = conv;
-    }
-
-    @Inject
-    public void setObjectFactory(ObjectFactory fac) {
-        this.objectFactory = fac;
-    }
-
-    @Inject
-    public void setObjectTypeDeterminer(ObjectTypeDeterminer ot) {
-        this.objectTypeDeterminer = ot;
-    }
-
-    @Override
-    public Object getProperty(Map context, Object target, Object name) throws OgnlException {
-
-        if (LOG.isDebugEnabled()) {
-            LOG.debug("Entering getProperty ("+context+","+target+","+name+")");
-        }
-
-        ReflectionContextState.updateCurrentPropertyPath(context, name);
-        // if this is one of the regular index access
-        // properties then just let the superclass deal with the
-        // get.
-        if (name instanceof String && contains(INDEX_ACCESS_PROPS, (String) name)) {
-            return super.getProperty(context, target, name);
-        }
-
-        Object result = null;
-
-        try{
-            result = super.getProperty(context, target, name);
-        } catch(Exception ex){
-        }
-
-        if (result == null) {
-            //find the key class and convert the name to that class
-            Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
-
-            String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
-            if (lastClass == null || lastProperty == null) {
-                return super.getProperty(context, target, name);
-            }
-            Class keyClass = objectTypeDeterminer
-                    .getKeyClass(lastClass, lastProperty);
-
-            if (keyClass == null) {
-
-                keyClass = java.lang.String.class;
-            }
-            Object key = getKey(context, name);
-            Map map = (Map) target;
-            result = map.get(key);
-
-            if (result == null &&
-                    context.get(ReflectionContextState.CREATE_NULL_OBJECTS) != null
-                    &&  objectTypeDeterminer.shouldCreateIfNew(lastClass,lastProperty,target,null,false)) {
-                Class valueClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, key);
-
-                try {
-                    result = objectFactory.buildBean(valueClass, context);
-                    map.put(key, result);
-                } catch (Exception exc) {
-
-                }
-
-            }
-        }
-        return result;
-    }
-
-    /**
-     * @param array
-     * @param name
-     */
-    private boolean contains(String[] array, String name) {
-        for (String anArray : array) {
-            if (anArray.equals(name)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
-        if (LOG.isDebugEnabled()) {
-     		LOG.debug("Entering setProperty("+context+","+target+","+name+","+value+")");
-     	}
-
-        Object key = getKey(context, name);
-        Map map = (Map) target;
-        map.put(key, getValue(context, value));
-     }
-
-     private Object getValue(Map context, Object value) {
-         Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
-         String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
-         if (lastClass == null || lastProperty == null) {
-             return value;
-         }
-         Class elementClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, null);
-         if (elementClass == null) {
-             return value; // nothing is specified, we assume it will be the value passed in.
-         }
-         return xworkConverter.convertValue(context, value, elementClass);
-}
-
-    private Object getKey(Map context, Object name) {
-        Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
-        String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
-        if (lastClass == null || lastProperty == null) {
-            // return java.lang.String.class;
-            // commented out the above -- it makes absolutely no sense for when setting basic maps!
-            return name;
-        }
-        Class keyClass = objectTypeDeterminer.getKeyClass(lastClass, lastProperty);
-        if (keyClass == null) {
-            keyClass = java.lang.String.class;
-        }
-
-        return xworkConverter.convertValue(context, name, keyClass);
-
-    }
-}
-

core/src/main/java/com/opensymphony/xwork2/parameters/accessor/ParametersCollectionPropertyAccessor.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+
+package com.opensymphony.xwork2.parameters.accessor;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * @author Gabe
+ */
+public class ParametersCollectionPropertyAccessor implements ParametersPropertyAccessor {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ParametersCollectionPropertyAccessor.class);
+    private static final String CONTEXT_COLLECTION_MAP = "xworkCollectionPropertyAccessorContextSetMap";
+
+    public static final String KEY_PROPERTY_FOR_CREATION = "makeNew";
+
+    //use a basic object Ognl property accessor here
+    //to access properties of the objects in the Set
+    //so that nothing is put in the context to screw things up
+    private ParametersObjectPropertyAccessor _accessor = new ParametersObjectPropertyAccessor();
+
+    private XWorkConverter xworkConverter;
+    private ObjectFactory objectFactory;
+    private ObjectTypeDeterminer objectTypeDeterminer;
+    private ReflectionProvider reflectionProvider;
+
+    @Inject
+    public void setReflectionProvider(ReflectionProvider reflectionProvider) {
+        this.reflectionProvider = reflectionProvider;
+    }
+
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+
+    @Inject
+    public void setObjectFactory(ObjectFactory fac) {
+        this.objectFactory = fac;
+    }
+
+    @Inject
+    public void setObjectTypeDeterminer(ObjectTypeDeterminer ot) {
+        this.objectTypeDeterminer = ot;
+    }
+
+    /**
+     * Gets the property of a Collection by indexing the collection
+     * based on a key property. For example, if the key property were
+     * 'id', this method would convert the key Object to whatever
+     * type the id property was, and then access the Set like it was
+     * a Map returning a JavaBean with the value of id property matching
+     * the input.
+     *
+     * @see ognl.PropertyAccessor#getProperty(java.util.Map, Object, Object)
+     */
+    @Override
+    public Object getProperty(Map context, Object target, Object key)
+            throws Exception {
+
+        LOG.debug("Entering getProperty()");
+
+        //check if it is a generic type property.
+        //if so, return the value from the
+        //superclass which will determine this.
+        if (!ReflectionContextState.isGettingByKeyProperty(context)
+                && !key.equals(KEY_PROPERTY_FOR_CREATION)) {
+            return reflectionProvider.getValue(key.toString(), context, target);
+        } else {
+            //reset context property
+            ReflectionContextState.setGettingByKeyProperty(context, false);
+        }
+        Collection c = (Collection) target;
+
+        //get the bean that this collection is a property of
+        Class lastBeanClass = ReflectionContextState.getLastBeanClassAccessed(context);
+
+        //get the property name that this collection uses
+        String lastPropertyClass = ReflectionContextState.getLastBeanPropertyAccessed(context);
+
+        //if one or the other is null, assume that it isn't
+        //set up correctly so just return whatever the
+        //superclass would
+        if (lastBeanClass == null || lastPropertyClass == null) {
+            ReflectionContextState.updateCurrentPropertyPath(context, key);
+            return reflectionProvider.getValue(key.toString(), context, target);
+        }
+
+
+        //get the key property to index the
+        //collection with from the ObjectTypeDeterminer
+        String keyProperty = objectTypeDeterminer
+                .getKeyProperty(lastBeanClass, lastPropertyClass);
+
+        //get the collection class of the
+        Class collClass = objectTypeDeterminer.getElementClass(lastBeanClass, lastPropertyClass, key);
+
+        Class keyType = null;
+        Class toGetTypeFrom = (collClass != null) ? collClass : c.iterator().next().getClass();
+        try {
+            keyType = reflectionProvider.getPropertyDescriptor(toGetTypeFrom, keyProperty).getPropertyType();
+        } catch (Exception exc) {
+            throw new Exception("Error getting property descriptor: " + exc.getMessage());
+        }
+
+
+        if (ReflectionContextState.isCreatingNullObjects(context)) {
+            Map collMap = getSetMap(context, c, keyProperty, collClass);
+            if (key.toString().equals(KEY_PROPERTY_FOR_CREATION)) {
+                //this should return the XWorkList
+                //for this set that contains new entries
+                //then the ListPropertyAccessor will be called
+                //to access it in the next sequence
+                return collMap.get(null);
+            }
+            Object realKey = xworkConverter.convertValue(context, key, keyType);
+            Object value = collMap.get(realKey);
+            if (value == null
+                    && ReflectionContextState.isCreatingNullObjects(context)
+                    && objectTypeDeterminer
+                    .shouldCreateIfNew(lastBeanClass, lastPropertyClass, c, keyProperty, false)) {
+                //create a new element and
+                //set the value of keyProperty
+                //to be the given value
+                try {
+                    value = objectFactory.buildBean(collClass, context);
+
+                    //set the value of the keyProperty
+                    _accessor.setProperty(context, value, keyProperty, realKey);
+
+                    //add the new object to the collection
+                    c.add(value);
+
+                    //add to the Map if accessed later
+                    collMap.put(realKey, value);
+
+
+                } catch (Exception exc) {
+                    throw new Exception("Error adding new element to collection", exc);
+
+                }
+
+            }
+            return value;
+        } else {
+            if (key.toString().equals(KEY_PROPERTY_FOR_CREATION)) {
+                return null;
+            }
+            //with getting do iteration
+            //don't assume for now it is
+            //optimized to create the Map
+            //and unlike setting, there is
+            //no easy key for the Set
+            Object realKey = xworkConverter.convertValue(context, key, keyType);
+            return getPropertyThroughIteration(context, c, keyProperty, realKey);
+        }
+    }
+
+    /*
+      * Gets an indexed Map by a given key property with the key being
+      * the value of the property and the value being the
+      */
+    private Map getSetMap(Map context, Collection collection, String property, Class valueClass)
+            throws Exception {
+        LOG.debug("getting set Map");
+        String path = ReflectionContextState.getCurrentPropertyPath(context);
+        Map map = ReflectionContextState.getSetMap(context,
+                path);
+
+        if (map == null) {
+            LOG.debug("creating set Map");
+            map = new HashMap();
+            map.put(null, new SurrugateList(collection));
+            for (Object currTest : collection) {
+                Object currKey = _accessor.getProperty(context, currTest, property);
+                if (currKey != null) {
+                    map.put(currKey, currTest);
+                }
+            }
+            ReflectionContextState.setSetMap(context, map, path);
+        }
+        return map;
+    }
+
+    /*
+      * gets a bean with the given
+      */
+    public Object getPropertyThroughIteration(Map context, Collection collection, String property, Object key)
+            throws Exception {
+        for (Object currTest : collection) {
+            if (_accessor.getProperty(context, currTest, property).equals(key)) {
+                return currTest;
+            }
+        }
+        //none found
+        return null;
+    }
+
+    @Override
+    public void setProperty(Map context, Object target, Object property, Object value)
+            throws Exception {
+
+       reflectionProvider.setProperty(property.toString(), value, target, context);
+    }
+}
+
+class SurrugateList extends ArrayList {
+
+    private Collection surrugate;
+
+    public SurrugateList(Collection surrugate) {
+        this.surrugate = surrugate;
+    }
+
+    @Override
+    public void add(int arg0, Object arg1) {
+        if (arg1 != null) {
+            surrugate.add(arg1);
+        }
+        super.add(arg0, arg1);
+    }
+
+    @Override
+    public boolean add(Object arg0) {
+        if (arg0 != null) {
+            surrugate.add(arg0);
+        }
+        return super.add(arg0);
+    }
+
+    @Override
+    public boolean addAll(Collection arg0) {
+        surrugate.addAll(arg0);
+        return super.addAll(arg0);
+    }
+
+    @Override
+    public boolean addAll(int arg0, Collection arg1) {
+        surrugate.addAll(arg1);
+        return super.addAll(arg0, arg1);
+    }
+
+    @Override
+    public Object set(int arg0, Object arg1) {
+        if (arg1 != null) {
+            surrugate.add(arg1);
+        }
+        return super.set(arg0, arg1);
+    }
+}

core/src/main/java/com/opensymphony/xwork2/parameters/accessor/ParametersCompoundRootAccessor.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.parameters.accessor;
+
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+
+
+import java.util.*;
+
+
+/**
+ * A stack that is able to call methods on objects in the stack.
+ *
+ * @author $Author: rainerh $
+ * @author Rainer Hermanns
+ * @version $Revision: 1836 $
+ */
+public class ParametersCompoundRootAccessor implements ParametersPropertyAccessor {
+
+    private final static Logger LOG = LoggerFactory.getLogger(ParametersCompoundRootAccessor.class);
+
+    static boolean devMode = false;
+
+    private ReflectionProvider reflectionProvider;
+
+    @Inject("devMode")
+    public static void setDevMode(String mode) {
+        devMode = "true".equals(mode);
+    }
+
+    public void setReflectionProvider(ReflectionProvider reflectionProvider) {
+        this.reflectionProvider = reflectionProvider;
+    }
+
+    public void setProperty(Map context, Object target, Object name, Object value) throws Exception {
+        CompoundRoot root = (CompoundRoot) target;
+
+        for (Object o : root) {
+            if (o == null) {
+                continue;
+            }
+            if (reflectionProvider.getSetMethod(o.getClass(), name.toString()) != null) {
+                reflectionProvider.setProperty(name.toString(), value, o, context);
+
+                return;
+            } else if (o instanceof Map) {
+                Map<Object, Object> map = (Map) o;
+                map.put(name, value);
+                return;
+            }
+
+        }
+
+        Boolean reportError = (Boolean) context.get(ValueStack.REPORT_ERRORS_ON_NO_PROP);
+
+        final String msg = "No object in the CompoundRoot has a publicly accessible property named '" + name + "' (no setter could be found).";
+
+        if ((reportError != null) && (reportError.booleanValue())) {
+            throw new XWorkException(msg);
+        } else {
+            if (devMode) {
+                LOG.warn(msg);
+            }
+        }
+    }
+
+    public Object getProperty(Map context, Object target, Object name) throws Exception {
+        CompoundRoot root = (CompoundRoot) target;
+
+        if (name instanceof Integer) {
+            Integer index = (Integer) name;
+
+            return root.cutStack(index.intValue());
+        } else if (name instanceof String) {
+            if ("top".equals(name)) {
+                if (root.size() > 0) {
+                    return root.get(0);
+                } else {
+                    return null;
+                }
+            }
+
+            for (Object o : root) {
+                if (o == null) {
+                    continue;
+                }
+
+                try {
+                    if ((reflectionProvider.getGetMethod(o.getClass(), name.toString()) != null) || ((o instanceof Map) && ((Map) o).containsKey(name))) {
+                        return reflectionProvider.getValue(name.toString(), context, root);
+                    }
+                } catch (Exception e) {
+                    final String msg = "Caught an Ognl exception while getting property " + name;
+                    throw new XWorkException(msg, e);
+                }
+            }
+
+            return null;
+        } else {
+            return null;
+        }
+    }
+}

core/src/main/java/com/opensymphony/xwork2/parameters/accessor/ParametersListPropertyAccessor.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.parameters.accessor;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+
+public class ParametersListPropertyAccessor implements ParametersPropertyAccessor {
+
+    private XWorkCollectionPropertyAccessor _sAcc = new XWorkCollectionPropertyAccessor();
+
+    private XWorkConverter xworkConverter;
+    private ObjectFactory objectFactory;
+    private ObjectTypeDeterminer objectTypeDeterminer;
+    private ReflectionProvider reflectionProvider;
+
+    @Inject
+    public void setReflectionProvider(ReflectionProvider reflectionProvider) {
+        this.reflectionProvider = reflectionProvider;
+    }
+
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+
+    @Inject
+    public void setObjectFactory(ObjectFactory fac) {
+        this.objectFactory = fac;
+    }
+
+    @Inject
+    public void setObjectTypeDeterminer(ObjectTypeDeterminer ot) {
+        this.objectTypeDeterminer = ot;
+    }
+
+
+    public Object getProperty(Map context, Object target, Object name) {
+        List list = (List) target;
+
+        Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+        String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+
+        Long numericValue = null;
+        try {
+            numericValue = Long.valueOf(name.toString());
+        } catch (NumberFormatException e) {
+            //ignore
+        }
+
+        if (numericValue != null
+                && ReflectionContextState.isCreatingNullObjects(context)
+                && objectTypeDeterminer.shouldCreateIfNew(lastClass, lastProperty, target, null, true)) {
+            int index = numericValue.intValue();
+            int listSize = list.size();
+
+            /*if (lastClass == null || lastProperty == null) {
+                return super.getProperty(context, target, name);
+            }*/
+            Class beanClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, name);
+            if (listSize <= index) {
+                Object result = null;
+
+                for (int i = listSize; i < index; i++) {
+                    list.add(null);
+                }
+                try {
+                    list.add(index, result = objectFactory.buildBean(beanClass, context));
+                } catch (Exception exc) {
+                    throw new XWorkException(exc);
+                }
+                return result;
+            } else if (list.get(index) == null) {
+                Object result = null;
+                try {
+                    list.set(index, result = objectFactory.buildBean(beanClass, context));
+                } catch (Exception exc) {
+                    throw new XWorkException(exc);
+                }
+                return result;
+            } else {
+                return list.get(index);
+            }
+        }
+
+        return null;
+    }
+
+
+    public void setProperty(Map context, Object target, Object name, Object value) {
+        Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+        String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+        Class convertToClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, name);
+
+        if (name instanceof String && value.getClass().isArray()) {
+            // looks like the input game in the form of "someList.foo" and
+            // we are expected to define the index values ourselves.
+            // So let's do it:
+
+            Collection c = (Collection) value;
+            Object[] values = (Object[]) value;
+            for (Object v : values) {
+                try {
+                    Object o = objectFactory.buildBean(convertToClass, context);
+                    reflectionProvider.setProperty((String) name, value, target, context);
+                    c.add(o);
+                } catch (Exception e) {
+                    throw new XWorkException("Error converting given String values for Collection.", e);
+                }
+            }
+        }
+
+        Object realValue = getRealValue(context, value, convertToClass);
+
+        Long numericValue = null;
+        try {
+            numericValue = Long.valueOf(name.toString());
+        } catch (NumberFormatException e) {
+            //ignore
+        }
+
+        if (target instanceof List && numericValue != null) {
+            //make sure there are enough spaces in the List to set
+            List list = (List) target;
+            int listSize = list.size();
+            int count = numericValue.intValue();
+            if (count >= listSize) {
+                for (int i = listSize; i <= count; i++) {
+                    list.add(null);
+                }
+            }
+
+            ((List) target).set(numericValue.intValue(), realValue);
+        }
+    }
+
+    private Object getRealValue(Map context, Object value, Class convertToClass) {
+        if (value == null || convertToClass == null) {
+            return value;
+        }
+        return xworkConverter.convertValue(context, value, convertToClass);
+    }
+}

core/src/main/java/com/opensymphony/xwork2/parameters/accessor/ParametersMapPropertyAccessor.java

+/*
+ * $Id$
+ *
+ * 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 com.opensymphony.xwork2.parameters.accessor;
+
+import ognl.OgnlException;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.inject.Inject;
+
+import java.util.Map;
+
+/**
+ * Same code as XWorkMapPropertyAccessor, but all exceptions are catched from
+ * result = super.getProperty(context, target, name);
+ * because we didn't parse the expression as an ONGL expression, calling getProperty might fail in some
+ * scenarios. This class is not intended to be used outside this package
+ */
+public class ParametersMapPropertyAccessor implements ParametersPropertyAccessor {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ParametersMapPropertyAccessor.class);
+
+    private XWorkConverter xworkConverter;
+    private ObjectFactory objectFactory;
+    private ObjectTypeDeterminer objectTypeDeterminer;
+
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+
+    @Inject
+    public void setObjectFactory(ObjectFactory fac) {
+        this.objectFactory = fac;
+    }
+
+    @Inject
+    public void setObjectTypeDeterminer(ObjectTypeDeterminer ot) {
+        this.objectTypeDeterminer = ot;
+    }
+
+    @Override
+    public Object getProperty(Map context, Object target, Object name) throws OgnlException {
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("Entering getProperty ("+context+","+target+","+name+")");
+        }
+
+        ReflectionContextState.updateCurrentPropertyPath(context, name);
+       
+        Object result = null;
+
+
+        if (result == null) {
+            //find the key class and convert the name to that class
+            Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+
+            String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+
+            Class keyClass = objectTypeDeterminer
+                    .getKeyClass(lastClass, lastProperty);
+
+            if (keyClass == null) {
+
+                keyClass = String.class;
+            }
+            Object key = getKey(context, name);
+            Map map = (Map) target;
+            result = map.get(key);
+
+            if (result == null &&
+                    context.get(ReflectionContextState.CREATE_NULL_OBJECTS) != null
+                    &&  objectTypeDeterminer.shouldCreateIfNew(lastClass,lastProperty,target,null,false)) {
+                Class valueClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, key);
+
+                try {
+                    result = objectFactory.buildBean(valueClass, context);
+                    map.put(key, result);
+                } catch (Exception exc) {
+
+                }
+
+            }
+        }
+        return result;
+    }
+
+    /**
+     * @param array
+     * @param name
+     */
+    private boolean contains(String[] array, String name) {
+        for (String anArray : array) {
+            if (anArray.equals(name)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
+        if (LOG.isDebugEnabled()) {
+     		LOG.debug("Entering setProperty("+context+","+target+","+name+","+value+")");
+     	}
+
+        Object key = getKey(context, name);
+        Map map = (Map) target;
+        map.put(key, getValue(context, value));
+     }
+
+     private Object getValue(Map context, Object value) {
+         Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+         String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+         if (lastClass == null || lastProperty == null) {
+             return value;
+         }
+         Class elementClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, null);
+         if (elementClass == null) {
+             return value; // nothing is specified, we assume it will be the value passed in.
+         }
+         return xworkConverter.convertValue(context, value, elementClass);
+}
+
+    private Object getKey(Map context, Object name) {
+        Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+        String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+        if (lastClass == null || lastProperty == null) {
+            // return java.lang.String.class;
+            // commented out the above -- it makes absolutely no sense for when setting basic maps!
+            return name;
+        }
+        Class keyClass = objectTypeDeterminer.getKeyClass(lastClass, lastProperty);
+        if (keyClass == null) {
+            keyClass = String.class;
+        }
+
+        return xworkConverter.convertValue(context, name, keyClass);
+
+    }
+}

core/src/main/java/com/opensymphony/xwork2/parameters/accessor/ParametersObjectPropertyAccessor.java

+package com.opensymphony.xwork2.parameters.accessor;
+
+
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+
+import java.util.Map;
+
+
+public class ParametersObjectPropertyAccessor implements ParametersPropertyAccessor {
+    protected ReflectionProvider reflectionProvider;
+
+    @Override
+    public Object getProperty(Map context, Object target, Object property) throws Exception {
+        Object obj = reflectionProvider.getValue(property.toString(), context, target);
+
+        context.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, target.getClass());
+        context.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, property.toString());
+        ReflectionContextState.updateCurrentPropertyPath(context, property);
+        return obj;
+    }
+
+    @Override
+    public void setProperty(Map context, Object target, Object property, Object value) throws Exception {
+        reflectionProvider.setProperty(property.toString(), value, target, context);
+    }
+
+    @Inject
+    public void setReflectionProvider(ReflectionProvider reflectionProvider) {
+        this.reflectionProvider = reflectionProvider;
+    }
+}

core/src/main/java/com/opensymphony/xwork2/parameters/accessor/ParametersPropertyAccessor.java

+package com.opensymphony.xwork2.parameters.accessor;
+
+import java.util.Map;
+
+public interface ParametersPropertyAccessor {
+    Object getProperty(Map context, Object lastObject, Object name) throws Exception;
+
+    void setProperty(Map context, Object target, Object property, Object value) throws Exception;
+}

core/src/test/java/com/opensymphony/xwork2/parameters/XWorkParametersBinderTest.java

 
 import com.opensymphony.xwork2.SimpleAction;
 import com.opensymphony.xwork2.XWorkTestCase;
+import com.opensymphony.xwork2.util.CompoundRoot;
 
 import java.util.HashMap;
 import java.util.Map;
         assertEquals("Lex Luthor", action.getName());
     }
 
+    public void testSimpleOnCompoundRoot() throws ParseException, OgnlException {
+        String expr = "name";
+        SimpleAction action = new SimpleAction();
+
+        CompoundRoot root = new CompoundRoot();
+        root.push(action);
+
+        assertNull(action.getName());
+
+        Map<String, Object> context = new HashMap<String, Object>();
+        binder.setProperty(context, action, expr, "Lex Luthor");
+
+        assertEquals("Lex Luthor", action.getName());
+    }
+
+
     public void testPropertyAsIndex() throws ParseException, OgnlException {
         String expr = "['name']";
         SimpleAction action = new SimpleAction();
         assertEquals("Lex Luthor", action.getSomeMap().get("Name"));
     }
 
-     public void testSimplePropertyOnObjectInMap() throws ParseException, OgnlException {
+    public void testSimplePropertyOnObjectInMap() throws ParseException, OgnlException {
         String expr = "otherMap['my_hero'].name";
         SimpleAction action = new SimpleAction();