Commits

Anonymous committed 8a81328

XW-534 (Updgrate Ognl dependency to 2.7 for XWork 1.2.4 release)

git-svn-id: http://svn.opensymphony.com/svn/xwork/branches/xwork_1-2@1684e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (6)

src/java/com/opensymphony/xwork/XWorkConstants.java

+package com.opensymphony.xwork;
+
+/**
+ * Contains constants used in {@link Configuration}'s parameters. eg.
+ *
+ * <pre>
+ *    &lt;xwork&gt;
+ *       &lt;parameters&gt;
+ *          &lt;parameter name="useOgnlEnhancement" value="false" /&gt;
+ *       &lt;/parameters&gt;
+ *    &lt;/xwork&gt;
+ * </pre>
+ *
+ * The "useOgnlEnhancement" parameter name is a constant in this class.
+ *
+ * @author tmjee
+ * @version $Date$ $Id$
+ */
+public class XWorkConstants {
+    
+    /**
+     * Parameter name of Ognl Enhancement. eg.
+     * <pre>
+     *  &lt;xwork&gt;
+     *       &lt;parameters&gt;
+     *          &lt;parameter name="useOgnlEnhancement" value="false" /&gt;
+     *       &lt;/parameters&gt;
+     *    &lt;/xwork&gt;
+     * </pre>
+     */
+    public static final String XWORK_USE_OGNL_ENHANCEMENT = "useOgnlEnhancement";
+}

src/java/com/opensymphony/xwork/util/CompoundRootAccessor.java

         CompoundRoot root = (CompoundRoot) target;
 
         if (name instanceof Integer) {
-            return ".cutStack("+name+")";
+            ExpressionCompiler.addCastString(ognlcontext, "((" + CompoundRoot.class.getName() + ")");
+            ognlcontext.setCurrentType(CompoundRoot.class);
+            ognlcontext.setCurrentAccessor(CompoundRoot.class);
+            return ".cutStack("+name+"))";
         }
         else if (name instanceof String) {
             String beanName = ((String)name).replaceAll("\"", "");
 
             try {
                 Integer.valueOf(beanName);
-                return ".cutStack("+name+")";
+                ExpressionCompiler.addCastString(ognlcontext, "((" + CompoundRoot.class.getName() + ")");
+                ognlcontext.setCurrentType(CompoundRoot.class);
+                ognlcontext.setCurrentAccessor(CompoundRoot.class);
+                return ".cutStack("+name+"))";
             }
             catch(NumberFormatException e) {
                 // ignore, its not a number
             }
 
             if ("top".equals(beanName)) {
-                return ".get(0)";
+                Object topObj = root.get(0);
+                if (topObj != null) {
+                    ExpressionCompiler.addCastString(ognlcontext, "((" + topObj.getClass().getName() + ")");
+                    ognlcontext.setCurrentType(topObj.getClass());
+                }
+                else {
+                    ExpressionCompiler.addCastString(ognlcontext, "((" + Object.class.getName() + ")");
+                    ognlcontext.setCurrentType(Object.class);
+                }
+                ognlcontext.setCurrentAccessor(CompoundRoot.class);
+                return ".get(0))";
             }
 
 
                 try {
                 Object tmp = i.next();
                     if (tmp != null) {
-                        PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(tmp.getClass(), beanName);
-                        if (pd != null) {
-                            if (Map.class.isAssignableFrom(tmp.getClass())) {
+                        if (Map.class.isAssignableFrom(tmp.getClass())) {
+                            Map tmpMap = (Map) tmp;
+                            if (tmpMap.containsKey(beanName)) {
 
-                                ExpressionCompiler.addCastString(ognlcontext, "(("+Map.class.getName()+")");
+                                ExpressionCompiler.addCastString(ognlcontext, "(("+tmpMap.get(beanName).getClass().getName()+")(("+Map.class.getName()+")");
 
-                                ognlcontext.setCurrentType(Map.class);
+                                ognlcontext.setCurrentType(tmpMap.get(beanName).getClass());
                                 ognlcontext.setCurrentAccessor(CompoundRoot.class);
 
-                                return ".get("+a+")).get(\""+beanName+"\")";
+                                return ".get("+a+")).get(\""+beanName+"\"))";
                             }
-
+                        }
+                        PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(tmp.getClass(), beanName);
+                        if (pd != null) {
                             Class type = OgnlRuntime.getCompiler().getSuperOrInterfaceClass(pd.getReadMethod(), tmp.getClass());
 
 
                 try {
                 Object tmp = i.next();
                     if (tmp != null) {
-                        PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(tmp.getClass(), beanName);
-                        if (pd != null) {
-                            if (Map.class.isAssignableFrom(tmp.getClass())) {
 
-                                ExpressionCompiler.addCastString(ognlcontext, "(("+Map.class.getName()+")");
+                        if (Map.class.isAssignableFrom(tmp.getClass())) {
 
-                                ognlcontext.setCurrentType(Map.class);
-                                ognlcontext.setCurrentAccessor(CompoundRoot.class);
+                            ExpressionCompiler.addCastString(ognlcontext, "(("+Map.class.getName()+")");
 
-                                return ".get("+a+")).get(\""+beanName+"\")";
-                            }
+                            ognlcontext.setCurrentType(Map.class);
+                            ognlcontext.setCurrentAccessor(CompoundRoot.class);
 
+                            return ".get("+a+")).put(\""+beanName+"\", $3)";
+                        }
+
+                        PropertyDescriptor pd = OgnlRuntime.getPropertyDescriptor(tmp.getClass(), beanName);
+                        if (pd != null) {
                             if (pd.getWriteMethod().getParameterTypes().length > 1) {
                                  throw new UnsupportedCompilationException("Object property accessors can only support single parameter setters.");
                             }

src/java/com/opensymphony/xwork/util/ObjectProxyPropertyAccessor.java

                 Class wrapClass = OgnlRuntime.getPrimitiveWrapperClass(param);
                 conversion = OgnlRuntime.getCompiler().createLocalReference(ognlcontext,
                                       "((" + wrapClass.getName() + ")ognl.OgnlOps#convertValue($3," + wrapClass.getName()
-                                      + ".class, true))." + OgnlRuntime.getNumericValueGetter(wrapClass),
+                                      + ".class, true))." + OgnlTools.getPrimitiveValueGetter(wrapClass),
                                       param);
 
             } else if (param.isArray()) {

src/java/com/opensymphony/xwork/util/OgnlUtil.java

 /*
- * Copyright (c) 2002-2006 by OpenSymphony
+ * Copyright (c) 2002-2007 by OpenSymphony
  * All rights reserved.
  */
 package com.opensymphony.xwork.util;
 
-import ognl.Ognl;
-import ognl.OgnlContext;
-import ognl.OgnlException;
-import ognl.OgnlRuntime;
+import com.opensymphony.xwork.XWorkConstants;
+import com.opensymphony.xwork.XworkException;
+import com.opensymphony.xwork.config.ConfigurationManager;
+import ognl.*;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import java.util.Iterator;
 import java.util.Map;
 
-import com.opensymphony.xwork.XworkException;
-
 
 /**
  * Utility class that provides common access to the <a href="www.ognl.org">Ognl</a> APIs for
 public class OgnlUtil {
 
     private static final Log log = LogFactory.getLog(OgnlUtil.class);
-    private static HashMap expressions = new HashMap();
+    private static HashMap parsedExpressions = new HashMap();
     private static HashMap beanInfoCache = new HashMap();
 
+    private static Boolean useOgnlEnhancement = null;
+
 
     /**
      * Sets the object's properties using the default type converter, defaulting to not throw
 
 
     /**
-     * Wrapper around Ognl.setValue() to handle type conversion for collection elements.
-     * Ideally, this should be handled by OGNL directly.
+     * Wrapper to use OGNL to set the property on target <code>root</code> based on
+     * OGNL expression <code>name</code> with the value as <code>value</code>, encapsulating if the
+     * expression were to be parsed or compiled depending on {@link #isUseOgnlEnhancement()}.
+     * 
+     * @param name
+     * @param context
+     * @param root
+     * @param value
      */
     public static void setValue(String name, Map context, Object root, Object value) throws OgnlException {
+        if (isUseOgnlEnhancement()) {
+            try {
+                Node node = Ognl.compileExpression((OgnlContext) context, root, name);
+                node.getAccessor().set((OgnlContext) context, root, value);
+                return;
+            }
+            catch(Exception e) {
+                log.warn("unable to set value using OGNL expression compilation mode, falling back to expression parsing", e);
+            }
+        }
         Ognl.setValue(compile(name), context, root, value);
     }
 
+    /**
+     * Wrapper to use OGNL to get the property on target <code>root</code> based on the OGNL expression
+     * <code>expression</code> encapsulating if the expression were to be parsed or compiled depending on
+     * {@link #isUseOgnlEnhancement()}. 
+     *
+     * @param name
+     * @param context
+     * @param root
+     * @return
+     * @throws OgnlException
+     */
     public static Object getValue(String name, Map context, Object root) throws OgnlException {
+        if (isUseOgnlEnhancement()) {
+            try {
+                Node node = Ognl.compileExpression((OgnlContext)context, root, name);
+                return node.getAccessor().get((OgnlContext)context, root);
+            }
+            catch(Exception e) {
+                log.warn("unable to get value using OGNL expression compilation mode, falling back to expression parsing", e); 
+            }
+        }
         return Ognl.getValue(compile(name), context, root);
     }
 
     public static Object getValue(String name, Map context, Object root, Class resultType) throws OgnlException {
+        if (isUseOgnlEnhancement()) {
+            try {
+                Node node = Ognl.compileExpression((OgnlContext)context, root, name);
+                return node.getAccessor().get((OgnlContext)context, root);
+            }
+            catch(Exception e) {
+                log.warn("unable to get value using OGNL expression compilation mode, falling back to expression parsing", e);
+            }
+        }
         return Ognl.getValue(compile(name), context, root, resultType);
     }
 
-
+    /**
+     * Parse an ognl expression specified as <code>expression</code>, cache the parsed result for
+     * better response next round.
+     * @param expression
+     * @return Object
+     * @throws OgnlException
+     */
     public static Object compile(String expression) throws OgnlException {
-        synchronized (expressions) {
-            Object o = expressions.get(expression);
+        synchronized (parsedExpressions) {
+            Object o = parsedExpressions.get(expression);
 
             if (o == null) {
                 o = Ognl.parseExpression(expression);
-                expressions.put(expression, o);
+                parsedExpressions.put(expression, o);
             }
 
             return o;
                     PropertyDescriptor toPd = (PropertyDescriptor) toPdHash.get(fromPd.getName());
                     if ((toPd != null) && (toPd.getWriteMethod() != null)) {
                         try {
-                            Object expr = OgnlUtil.compile(fromPd.getName());
-                            Object value = Ognl.getValue(expr, contextFrom, from);
-                            Ognl.setValue(expr, contextTo, to, value);
+
+                            // === 1] get value
+                            Object value = null;
+                            boolean tryExpressionParsing = true;
+                            if (isUseOgnlEnhancement()) {
+                                try {
+                                    Node node = Ognl.compileExpression((OgnlContext)contextFrom, from, fromPd.getName());
+                                    value = node.getAccessor().get((OgnlContext)contextFrom, from);
+                                    tryExpressionParsing = false;
+                                }
+                                catch(Exception e) {
+                                    // let's try parsing ognl expression instead
+                                    tryExpressionParsing = true;
+                                }
+                            }
+                            if (tryExpressionParsing) {
+                                Object expr = OgnlUtil.compile(fromPd.getName());
+                                value = Ognl.getValue(expr, contextFrom, from);
+                            }
+
+
+                            // === 2] set value
+                            tryExpressionParsing = true;
+                            if (isUseOgnlEnhancement()) {
+                                try {
+                                    Node node = Ognl.compileExpression((OgnlContext)contextTo, to, fromPd.getName());
+                                    node.getAccessor().set((OgnlContext)contextTo, to, value);
+                                    tryExpressionParsing = false;
+                                }
+                                catch(Exception e) {
+                                    // let's try parsing ognl expression instead
+                                    tryExpressionParsing = true;
+                                }
+                            }
+                            if (tryExpressionParsing) {
+                                Object expr = OgnlUtil.compile(fromPd.getName());
+                                Ognl.setValue(expr, contextTo, to, value);
+                            }
                         } catch (OgnlException e) {
                             // ignore, this is OK
                         }
             String propertyName = propertyDescriptor.getDisplayName();
             Method readMethod = propertyDescriptor.getReadMethod();
             if (readMethod != null) {
-                Object expr = OgnlUtil.compile(propertyName);
-                Object value = Ognl.getValue(expr, sourceMap, source);
+
+                Object value = null;
+                boolean tryExpressionParsing = true;
+                if (isUseOgnlEnhancement()) {
+                    try {
+                        Node node = Ognl.compileExpression((OgnlContext)sourceMap, source, propertyName);
+                        value = node.getAccessor().get((OgnlContext)sourceMap, source);
+                        tryExpressionParsing=false;
+                    }
+                    catch(Exception e) {
+                        // let's try expression parsing
+                        tryExpressionParsing = true;
+                    }
+                }
+                if (tryExpressionParsing) {
+                    Object expr = OgnlUtil.compile(propertyName);
+                    value = Ognl.getValue(expr, sourceMap, source);
+                }
                 beanMap.put(propertyName, value);
             } else {
                 beanMap.put(propertyName, "There is no read method for " + propertyName);
         }
     }
 
+    /**
+     * An internal method to set the property whose name is <code>name</code>
+     * with value as <code>value</code> into target <code>o</code> with OGNL context
+     * map as <code>context</code>. If <code>throwPropertyExceptions</code> is true, we
+     * shall throws an exception if we failed to set the property esle we'll just log a
+     * warning.
+     * 
+     * @param name
+     * @param value
+     * @param o
+     * @param context
+     * @param throwPropertyExceptions
+     */
     static void internalSetProperty(String name, Object value, Object o, Map context, boolean throwPropertyExceptions) {
         try {
             setValue(name, context, o, value);
             }
         }
     }
+
+    /**
+     * Determine if we should use OGNL (2.7.x) ehhancement feature (compiled expression) instead of
+     * the default expression parsing.
+     * @return boolean true to use expression compilation false to use expression parsing.
+     */
+    static boolean isUseOgnlEnhancement() {
+        if (useOgnlEnhancement == null) {
+            String value = ConfigurationManager.getConfiguration().getParameter(XWorkConstants.XWORK_USE_OGNL_ENHANCEMENT);
+            if (value != null && "true".equalsIgnoreCase(value)) {
+                useOgnlEnhancement = Boolean.TRUE;
+            }
+            else {
+                useOgnlEnhancement = Boolean.FALSE; // by default, if not specified, its turn off
+            }
+        }
+        return useOgnlEnhancement.booleanValue();
+    }
 }

src/test/com/opensymphony/xwork/util/CompoundRootPropertyAccessorTest.java

 import ognl.OgnlContext;
 import ognl.OgnlRuntime;
 
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
 /**
+ * There's an issue with OGNL checkout in  opensymphony svn at http://svn.opensymphony.com/svn/ognl/trunk
+ * - first OgnlRuntime
+ *          public static Object callMethod(OgnlContext context, Object target,
+ *                                   String methodName, String propertyName, Object[] args)  throws OgnlException
+ *    doesn't exists anymore but instead replaced by
+ *          public static Object callMethod(OgnlContext context, Object target,
+ *                                   String methodName, Object[] args)
+ *
+ *     See http://forums.opensymphony.com/thread.jspa?threadID=167692&tstart=0 for more info
+ *
+ * - ListPropertyAccessor, ArrayPropertyAccessor uses
+ *    OgnlRuntime.getNumericValueGetter(....) which doesn't take into account primitive characters etc. 
+ *
+ *   See http://forums.opensymphony.com/thread.jspa?threadID=167693&tstart=0 for more info
+ *
  * @author tmjee
  * @version $Date$ $Id$
  */
 public class CompoundRootPropertyAccessorTest extends XWorkTestCase {
 
 
-    public void testBasic() throws Exception {
+    public void testGetterBasic() throws Exception {
         
         MyPrimitivesObject o = new MyPrimitivesObject();
         o.setMyShort((short)9);
     }
 
 
-    public void testArray() throws Exception {
+    public void testGetterArray() throws Exception {
         CompoundRootAccessor accessor = new CompoundRootAccessor();
 
         MyPrimitiveArrayObject o = new MyPrimitiveArrayObject();
     }
 
 
-    public void testObjects() throws Exception {
+    public void testGetterObjects() throws Exception {
         
         MyPrimitivesObject myPrimitiveObject = new MyPrimitivesObject();
         myPrimitiveObject.setMyLong(2l);
     }
 
 
-    public void testSpecialExpression() throws Exception {
-        Person p1 = new Person();
-        p1.setName("tmjee");
-        p1.setAge(new Integer(28));
+    public void testGetterSpecialExpression() throws Exception {
+
+        MyPrimitivesObject myPrimitiveObject1 = new MyPrimitivesObject();
+        myPrimitiveObject1.setMyLong(2l);
+        myPrimitiveObject1.setMyLongObject(new Long(3l));
+
+        MyPrimitiveArrayObject myPrimitiveArrayObject1 = new MyPrimitiveArrayObject();
+        myPrimitiveArrayObject1.setMyFloat(new float[] { 1.1f, 2.2f, 3.3f });
+        myPrimitiveArrayObject1.setMyFloatObject(new Float[] { new Float(4.4f), new Float(5.5f), new Float(6.6f) });
 
         Address a1 = new Address();
         a1.setStreet("High Street");
         a1.setPobox("1111");
+        a1.setMyArrayObject(myPrimitiveArrayObject1);
 
-        Person p2 = new Person();
-        p2.setName("phil");
-        p2.setAge(new Integer(40));
+        Person p1 = new Person();
+        p1.setName("tmjee");
+        p1.setAge(new Integer(28));
+        p1.setAddress(a1);
+        p1.setMyPrimitiveObject(myPrimitiveObject1);
+
+        MyPrimitivesObject myPrimitiveObject2 = new MyPrimitivesObject();
+        myPrimitiveObject2.setMyLong(2l);
+        myPrimitiveObject2.setMyLongObject(new Long(3l));
+
+        MyPrimitiveArrayObject myPrimitiveArrayObject2 = new MyPrimitiveArrayObject();
+        myPrimitiveArrayObject2.setMyFloat(new float[] { 1.1f, 2.2f, 3.3f });
+        myPrimitiveArrayObject2.setMyFloatObject(new Float[] { new Float(4.4f), new Float(5.5f), new Float(6.6f) });
 
         Address a2 = new Address();
         a2.setStreet("Melbourne Street");
         a2.setPobox("222");
+        a2.setMyArrayObject(myPrimitiveArrayObject2);
+
+        Person p2 = new Person();
+        p2.setName("phil");
+        p2.setAge(new Integer(40));
+        p2.setAddress(a2);
+        p2.setMyPrimitiveObject(myPrimitiveObject2);
 
 
         CompoundRoot root = new CompoundRoot();
             assertEquals(((CompoundRoot)node.getAccessor().get(ognlContext, root)).get(2), a2);
         }
 
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "top.name");
+            assertEquals(node.getAccessor().get(ognlContext, root), "tmjee");
+        }
+
+        {
+            Node node= Ognl.compileExpression(ognlContext, root, "top.address.street");
+            assertEquals(node.getAccessor().get(ognlContext, root), "High Street");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "top.myPrimitiveObject.myLong");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Long(2l));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "top.myPrimitiveObject.myLongObject");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Long(3l));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "top.address.myArrayObject.myFloat[0]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(1.1f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "top.address.myArrayObject.myFloat[1]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(2.2f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "top.address.myArrayObject.myFloat[2]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(3.3f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "top.address.myArrayObject.myFloatObject[0]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(4.4f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "top.address.myArrayObject.myFloatObject[1]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(5.5f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "top.address.myArrayObject.myFloatObject[2]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(6.6f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].name");
+            assertEquals(node.getAccessor().get(ognlContext, root), "phil");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].address.street");
+            assertEquals(node.getAccessor().get(ognlContext, root), "Melbourne Street");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].myPrimitiveObject.myLong");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Long(2l));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].myPrimitiveObject.myLongObject");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Long(3l));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].address.myArrayObject.myFloat[0]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(1.1f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].address.myArrayObject.myFloat[1]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(2.2f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].address.myArrayObject.myFloat[2]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(3.3f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].address.myArrayObject.myFloatObject[0]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(4.4f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].address.myArrayObject.myFloatObject[1]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(5.5f));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "[2].address.myArrayObject.myFloatObject[2]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Float(6.6f));
+        }
+    }
+
+
+    public void testGetterList() throws Exception {
+        CompoundRootAccessor accessor = new CompoundRootAccessor();
+
+        final Person p = new Person();
+        p.setName("tmjee");
+
+        MyPrimitiveArrayObject o = new MyPrimitiveArrayObject();
+        o.setMyList(new ArrayList() {
+            {
+                add("a string");
+                add(new Integer(1));
+                add(p);
+            }
+        });
+        Address a = new Address();
+        a.setMyArrayObject(o);
+
+        OgnlRuntime.setPropertyAccessor(CompoundRootAccessor.class, accessor);
+
+        CompoundRoot root = new CompoundRoot();
+        root.add(a);
+
+        OgnlContext ognlContext = (OgnlContext) Ognl.createDefaultContext(root);
+
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myArrayObject.myList.size");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Integer(3));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myArrayObject.myList[0]");
+            assertEquals(node.getAccessor().get(ognlContext, root), "a string");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myArrayObject.myList.get(1)");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Integer(1));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myArrayObject.myList[2].name");
+            assertEquals(node.getAccessor().get(ognlContext, root), "tmjee");
+        }
+
+    }
+
+    public void testGetterMap() throws Exception {
+        CompoundRootAccessor accessor = new CompoundRootAccessor();
+
+        final Person p = new Person();
+        p.setName("tmjee");
+
+        MyPrimitiveArrayObject o = new MyPrimitiveArrayObject();
+        o.setMyMap(new LinkedHashMap() {
+            {
+                put("key1", "a string");
+                put("key2", new Integer(1));
+                put("key3", p);
+            }
+        });
+
+        Address a = new Address();
+        a.setMyArrayObject(o);
+
+        OgnlRuntime.setPropertyAccessor(CompoundRootAccessor.class, accessor);
+
+        CompoundRoot root = new CompoundRoot();
+        root.add(a);
+
+        OgnlContext ognlContext = (OgnlContext) Ognl.createDefaultContext(root);
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myArrayObject.myMap.size");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Integer(3));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myArrayObject.myMap['key1']");
+            assertEquals(node.getAccessor().get(ognlContext, root), "a string");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myArrayObject.myMap.get('key2')");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Integer(1));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myArrayObject.myMap['key3'].name");
+            assertEquals(node.getAccessor().get(ognlContext, root), "tmjee");
+        }
+
+    }
+
+
+    public void testSetterBasic() throws Exception {
+
+        MyPrimitivesObject p = new MyPrimitivesObject();
+
+        CompoundRoot root = new CompoundRoot();
+        root.add(p);
+
+        CompoundRootAccessor accessor = new CompoundRootAccessor();
+        OgnlRuntime.setPropertyAccessor(CompoundRoot.class, accessor);
+
+        OgnlContext ognlContext = (OgnlContext) Ognl.createDefaultContext(root);
+
+
+        {  // int
+           assertEquals(p.getMyInteger(), 0);
+           Node node = Ognl.compileExpression(ognlContext, root, "myInteger"); 
+           node.getAccessor().set(ognlContext, root, new Integer(1));
+           assertEquals(p.getMyInteger(), 1);
+        }
+
+        {   // Integer
+            assertEquals(p.getMyIntegerObject(), null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myIntegerObject");
+            node.getAccessor().set(ognlContext, root, new Integer(2));
+            assertEquals(p.getMyIntegerObject(), new Integer(2));
+        }
+
+        {   // byte
+            assertEquals(p.getMyByte(), (byte)0);
+            Node node = Ognl.compileExpression(ognlContext, root, "myByte");
+            node.getAccessor().set(ognlContext, root, new Byte((byte)1));
+            assertEquals(p.getMyByte(), (byte)1);
+        }
+
+        {  // Byte
+            assertEquals(p.getMyByteObject(), null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myByteObject");
+            node.getAccessor().set(ognlContext, root, new Byte((byte)2));
+            assertEquals(p.getMyByteObject(), new Byte((byte)2));
+        }
+
+        {   // short
+            assertEquals(p.getMyShort(), (short)0);
+            Node node = Ognl.compileExpression(ognlContext, root, "myShort") ;
+            node.getAccessor().set(ognlContext, root, new Short((short)1));
+            assertEquals(p.getMyShort(), (short)1);
+        }
+
+        {   // Short
+            assertEquals(p.getMyShortObject(), null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myShortObject");
+            node.getAccessor().set(ognlContext, root, new Short((short)2));
+            assertEquals(p.getMyShortObject(), new Short((short)2));
+        }
+
+        {   // char
+            assertEquals(p.getMyCharacter(), (char)0);
+            Node node = Ognl.compileExpression(ognlContext, root, "myCharacter");
+            node.getAccessor().set(ognlContext, root, new Character('a'));
+            assertEquals(p.getMyCharacter(), 'a');
+        }
+
+        {   // Character
+            assertEquals(p.getMyCharacterObject(), null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myCharacterObject");
+            node.getAccessor().set(ognlContext, root, new Character('b'));
+            assertEquals(p.getMyCharacterObject(), new Character('b'));
+        }
+
+        {   // long
+            assertEquals(p.getMyLong(), 0l);
+            Node node = Ognl.compileExpression(ognlContext, root, "myLong");
+            node.getAccessor().set(ognlContext, root, new Long(1));
+            assertEquals(p.getMyLong(), 1l);
+        }
+
+        {   // Long
+            assertEquals(p.getMyLongObject(), null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myLongObject");
+            node.getAccessor().set(ognlContext, root, new Long(2));
+            assertEquals(p.getMyLongObject(), new Long(2));
+        }
+
+        {   // float
+            assertEquals(p.getMyFloat(), 0f, 2f);
+            Node node = Ognl.compileExpression(ognlContext, root, "myFloat");
+            node.getAccessor().set(ognlContext, root, new Float(1f));
+            assertEquals(p.getMyFloat(), 1f, 2f);
+        }
+
+        {   // Float
+            assertEquals(p.getMyFloatObject(), null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myFloatObject");
+            node.getAccessor().set(ognlContext, root, new Float(2f));
+            assertEquals(p.getMyFloatObject(), new Float(2f));
+        }
+
+        {   // double
+            assertEquals(p.getMyDouble(), 0d, 2d);
+            Node node = Ognl.compileExpression(ognlContext, root, "myDouble");
+            node.getAccessor().set(ognlContext, root, new Double(1d));
+            assertEquals(p.getMyDouble(), 1d, 2d);
+        }
+
+        {   // Double
+            assertEquals(p.getMyDoubleObject(), null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myDoubleObject");
+            node.getAccessor().set(ognlContext, root, new Double(2d));
+            assertEquals(p.getMyDoubleObject(), new Double(2d));
+        }
+
+        {   // String
+            assertEquals(p.getMyString(), null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myString");
+            node.getAccessor().set(ognlContext, root, "some string");
+            assertEquals(p.getMyString(), "some string");
+        }
+    }
+
+    public void testSetterArray() throws Exception {
+        MyPrimitiveArrayObject p = new MyPrimitiveArrayObject();
+        p.setMyShort(new short[2]);
+        p.setMyShortObject(new Short[2]);
+        p.setMyByte(new byte[2]);
+        p.setMyByteObject(new Byte[2]);
+        p.setMyCharacter(new char[2]);
+        p.setMyCharacterObject(new Character[2]);
+        p.setMyInteger(new int[2]);
+        p.setMyIntegerObject(new Integer[2]);
+        p.setMyLong(new long[2]);
+        p.setMyLongObject(new Long[2]);
+        p.setMyFloat(new float[2]);
+        p.setMyFloatObject(new Float[2]);
+        p.setMyDouble(new double[2]);
+        p.setMyDoubleObject(new Double[2]);
+        p.setMyString(new String[2]);
+
+
+        CompoundRoot root = new CompoundRoot();
+        root.add(p);
+
+        CompoundRootAccessor accessor = new CompoundRootAccessor();
+        OgnlRuntime.setPropertyAccessor(CompoundRoot.class, accessor);
+
+        OgnlContext ognlContext = (OgnlContext) Ognl.createDefaultContext(root);
+
+        {
+            assertEquals(p.getMyShort()[0], (short)0);
+            Node node = Ognl.compileExpression(ognlContext, root, "myShort[0]");
+            node.getAccessor().set(ognlContext, root, new Short((short)2));
+            assertEquals(p.getMyShort()[0], (short)2);
+        }
+
+        {
+            assertEquals(p.getMyShortObject()[0], null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myShortObject[0]");
+            node.getAccessor().set(ognlContext, root, new Short((short)1));
+            assertEquals(p.getMyShortObject()[0], new Short((short)1));
+        }
+
+        {
+            assertEquals(p.getMyByte()[0], (byte)0);
+            Node node = Ognl.compileExpression(ognlContext, root, "myByte[0]");
+            node.getAccessor().set(ognlContext, root, new Byte((byte)1));
+            assertEquals(p.getMyByte()[0], (byte)1);
+        }
+
+        {
+            assertEquals(p.getMyByteObject()[0], null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myByteObject[0]");
+            node.getAccessor().set(ognlContext, root, new Byte((byte)1));
+            assertEquals(p.getMyByteObject()[0], new Byte((byte)1));
+        }
+
+        /*{
+            // NOTE: We need to uncomment this when ognl could handle character properly.
+
+
+            assertEquals(p.getMyCharacter()[0], (char)0);
+            Node node = Ognl.compileExpression(ognlContext, root, "myCharacter[0]");
+            node.getAccessor().set(ognlContext, root, new Character('a'));
+            assertEquals(p.getMyCharacter()[0], 'a');
+        }*/
+
+        {
+            assertEquals(p.getMyCharacterObject()[0], null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myCharacterObject[0]");
+            node.getAccessor().set(ognlContext, root, new Character('a'));
+            assertEquals(p.getMyCharacterObject()[0], new Character('a'));
+        }
+
+        {
+            assertEquals(p.getMyInteger()[0], 0);
+            Node node = Ognl.compileExpression(ognlContext, root, "myInteger[0]");
+            node.getAccessor().set(ognlContext, root, new Integer(1));
+            assertEquals(p.getMyInteger()[0], 1);
+        }
+
+        {
+            assertEquals(p.getMyIntegerObject()[0], null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myIntegerObject[0]");
+            node.getAccessor().set(ognlContext, root, new Integer(1));
+            assertEquals(p.getMyIntegerObject()[0], new Integer(1));
+        }
+
+        {
+            assertEquals(p.getMyLong()[0], 0l);
+            Node node = Ognl.compileExpression(ognlContext, root, "myLong[0]");
+            node.getAccessor().set(ognlContext, root, new Long(1));
+            assertEquals(p.getMyLong()[0], 1l);
+        }
+
+        {
+            assertEquals(p.getMyLongObject()[0], null);
+            Node node=  Ognl.compileExpression(ognlContext, root, "myLongObject[0]");
+            node.getAccessor().set(ognlContext, root, new Long(1));
+            assertEquals(p.getMyLongObject()[0], new Long(1));
+        }
+
+        {
+            assertEquals(p.getMyFloat()[0], 0f, 2f);
+            Node node = Ognl.compileExpression(ognlContext, root, "myFloat[0]");
+            node.getAccessor().set(ognlContext, root, new Float(1f));
+            assertEquals(p.getMyFloat()[0], 1f, 2f);
+        }
+
+        {
+            assertEquals(p.getMyFloatObject()[0], null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myFloatObject[0]");
+            node.getAccessor().set(ognlContext, root, new Float(1f));
+            assertEquals(p.getMyFloatObject()[0], new Float(1f));    
+        }
+
+        {
+            assertEquals(p.getMyDouble()[0], 0d, 2d);
+            Node node = Ognl.compileExpression(ognlContext, root, "myDouble[0]");
+            node.getAccessor().set(ognlContext, root, new Double(1d));
+            assertEquals(p.getMyDouble()[0], 1d, 2d);
+        }
+
+        {
+            assertEquals(p.getMyDoubleObject()[0], null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myDoubleObject[0]");
+            node.getAccessor().set(ognlContext, root, new Double(1d));
+            assertEquals(p.getMyDoubleObject()[0], new Double(1d));
+        }
+
+        {
+            assertEquals(p.getMyString()[0], null);
+            Node node = Ognl.compileExpression(ognlContext, root, "myString[0]");
+            node.getAccessor().set(ognlContext, root, "hello tmjee");
+            assertEquals(p.getMyString()[0], "hello tmjee");
+        }
+    }
+
+
+    public void testSetterObjectsTest() throws Exception {
+
+        MyPrimitivesObject po = new MyPrimitivesObject();
+
+        MyPrimitiveArrayObject pao = new MyPrimitiveArrayObject();
+        pao.setMyInteger(new int[2]);
+        pao.setMyIntegerObject(new Integer[2]);
+        pao.setMyList(new ArrayList(){
+            {
+                add("xxx");
+            }
+        });
+        pao.setMyMap(new LinkedHashMap());
+
+        Address a = new Address();
+        a.setMyArrayObject(pao);
+
+        Person p = new Person();
+        p.setAddress(a);
+        p.setMyPrimitiveObject(po);
+
+        CompoundRootAccessor accessor = new CompoundRootAccessor();
+        OgnlRuntime.setPropertyAccessor(CompoundRoot.class, accessor);
+        CompoundRoot root = new CompoundRoot();
+        root.add(p);
+
+        OgnlContext ognlContext = (OgnlContext) Ognl.createDefaultContext(root);
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "name");
+            node.getAccessor().set(ognlContext, root, "tmjee");
+            assertEquals(p.getName(), "tmjee");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "address.street");
+            node.getAccessor().set(ognlContext, root, "High Street");
+            assertEquals(p.getAddress().getStreet(), "High Street");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myPrimitiveObject.myInteger");
+            node.getAccessor().set(ognlContext, root, new Integer(2));
+            assertEquals(p.getMyPrimitiveObject().getMyInteger(), 2);
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "myPrimitiveObject.myIntegerObject");
+            node.getAccessor().set(ognlContext, root, new Integer(1));
+            assertEquals(p.getMyPrimitiveObject().getMyIntegerObject(), new Integer(1));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "address.myArrayObject.myInteger[0]");
+            node.getAccessor().set(ognlContext, root, new Integer(3));
+            assertEquals(p.getAddress().getMyArrayObject().getMyInteger()[0], 3);
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "address.myArrayObject.myIntegerObject[1]");
+            node.getAccessor().set(ognlContext, root, new Integer(4));
+            assertEquals(p.getAddress().getMyArrayObject().getMyIntegerObject()[1], new Integer(4));
+        }
 
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "address.myArrayObject.myList[0]");
+            node.getAccessor().set(ognlContext, root, "happy birthday");
+            assertEquals(p.getAddress().getMyArrayObject().getMyList().get(0), "happy birthday");
+        }
+
+       {
+            Node node = Ognl.compileExpression(ognlContext, root, "address.myArrayObject.myMap['Key1']");
+            node.getAccessor().set(ognlContext, root, "happy valentine");
+            assertEquals(p.getAddress().getMyArrayObject().getMyMap().get("Key1"), "happy valentine");
+        }
     }
 
+
+    public void testGetterMapInCompoundRoot() throws Exception {
+
+        CompoundRootAccessor accessor = new CompoundRootAccessor();
+
+        MyPrimitivesObject po = new MyPrimitivesObject();
+        po.setMyLong(20l);
+
+        MyPrimitiveArrayObject pao = new MyPrimitiveArrayObject();
+         pao.setMyInteger(new int[] { 1, 2 });
+         pao.setMyIntegerObject(new Integer[] { new Integer(1), new Integer(2) });
+
+        Address a = new Address();
+        a.setMyArrayObject(pao);
+
+        Person p = new Person();
+        p.setName("phil");
+        p.setMyPrimitiveObject(po);
+        p.setAddress(a);
+
+        Person p2 = new Person();
+        p2.setName("George");
+
+        Map map = new LinkedHashMap();
+        map.put("key1", "tmjee");
+        map.put("key2", new Integer(10));
+        map.put("key3", p);
+
+        CompoundRoot root=  new CompoundRoot();
+        root.add(map);
+        root.add(p2);
+
+        OgnlContext ognlContext = (OgnlContext) Ognl.createDefaultContext(root);
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "key1");
+            assertEquals(node.getAccessor().get(ognlContext, root), "tmjee");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "name");
+            assertEquals(node.getAccessor().get(ognlContext, root), "George");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "key2");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Integer(10));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "key3.myPrimitiveObject.myLong");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Long(20l));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "key3.name");
+            assertEquals(node.getAccessor().get(ognlContext, root), "phil");
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "key3.address.myArrayObject.myInteger[0]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Integer(1));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "key3.address.myArrayObject.myInteger[1]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Integer(2));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "key3.address.myArrayObject.myIntegerObject[0]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Integer(1));
+        }
+
+        {
+            Node node = Ognl.compileExpression(ognlContext, root, "key3.address.myArrayObject.myIntegerObject[1]");
+            assertEquals(node.getAccessor().get(ognlContext, root), new Integer(2));
+        }
+    }
+
+     public void testSetterMapInCompoundRoot() throws Exception {
+
+        CompoundRootAccessor accessor = new CompoundRootAccessor();
+
+        MyPrimitivesObject po = new MyPrimitivesObject();
+
+        MyPrimitiveArrayObject pao = new MyPrimitiveArrayObject();
+         pao.setMyInteger(new int[2]);
+         pao.setMyIntegerObject(new Integer[2]);
+
+        Address a = new Address();
+        a.setMyArrayObject(pao);         
+
+
+        Person p = new Person();
+        p.setMyPrimitiveObject(po);
+        p.setAddress(a);
+
+        Map map = new LinkedHashMap();
+
+        CompoundRoot root=  new CompoundRoot();
+        root.add(p);
+        root.add(map);
+
+        OgnlContext ognlContext = (OgnlContext) Ognl.createDefaultContext(root);
+
+         {
+             Node node = Ognl.compileExpression(ognlContext, root, "key1");
+             node.getAccessor().set(ognlContext, root, "testing 123");
+             assertEquals(map.get("key1"), "testing 123");
+         }
+
+         {
+             Node node = Ognl.compileExpression(ognlContext, root, "name");
+             node.getAccessor().set(ognlContext, root, "tmjee");
+             assertEquals(p.getName(), "tmjee");
+         }
+
+         {
+             Node node = Ognl.compileExpression(ognlContext, root, "myPrimitiveObject.myInteger");
+             node.getAccessor().set(ognlContext, root, new Integer(22));
+             assertEquals(p.getMyPrimitiveObject().getMyInteger(), 22);
+         }
+
+         {
+             Node node = Ognl.compileExpression(ognlContext, root, "address.myArrayObject.myInteger[0]");
+             node.getAccessor().set(ognlContext, root, new Integer(22));
+             assertEquals(p.getAddress().getMyArrayObject().getMyInteger()[0], 22);
+         }
+
+         {
+             Node node = Ognl.compileExpression(ognlContext, root, "address.myArrayObject.myIntegerObject[0]");
+             node.getAccessor().set(ognlContext, root, new Integer(22));
+             assertEquals(p.getAddress().getMyArrayObject().getMyIntegerObject()[0], new Integer(22));
+         }
+
+     }
+
 }

src/test/com/opensymphony/xwork/util/MyPrimitiveArrayObject.java

  */
 package com.opensymphony.xwork.util;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * @author tmjee
  * @version $Date$ $Id$
     private Boolean[] myBooleanObject;
 
 
+    private List myList;
+    private Map myMap;
+
+    public List getMyList() {
+        return myList;
+    }
+
+    public void setMyList(List myList) {
+        this.myList = myList;
+    }
+
+    public Map getMyMap() {
+        return myMap;
+    }
+
+    public void setMyMap(Map myMap) {
+        this.myMap = myMap;
+    }
+
     public boolean[] getMyBoolean() {
         return myBoolean;
     }