rename classes, add more tests

git-svn-id: https://svn.apache.org/repos/asf/struts/sandbox/trunk@833776 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELReflectionContextFactory.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELReflectionContextFactory.java
new file mode 100644
index 0000000..0bdaad4
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELReflectionContextFactory.java
@@ -0,0 +1,15 @@
+package org.apache.struts2.uelplugin;
+
+import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * ReflectionContextFactory for Unified EL.
+ */
+public class UELReflectionContextFactory implements ReflectionContextFactory {
+    public Map createDefaultContext(Object root) {
+        return new HashMap();
+    }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELServletContextListener.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELServletContextListener.java
new file mode 100644
index 0000000..a48fcd7
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELServletContextListener.java
@@ -0,0 +1,33 @@
+package org.apache.struts2.uelplugin;
+
+import de.odysseus.el.ExpressionFactoryImpl;
+import de.odysseus.el.tree.TreeBuilder;
+
+import javax.el.ExpressionFactory;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import java.util.Properties;
+
+import org.apache.struts2.uelplugin.ExpressionFactoryHolder;
+
+/**
+ * Responsible for creating the ExpressionFactory that will be used by the
+ * UelValueStack
+ */
+public class UELServletContextListener implements ServletContextListener {
+    public void contextInitialized(ServletContextEvent contextEvent) {
+        Properties juelProperties = new Properties();
+        juelProperties.setProperty("javax.el.methodInvocations", "true");
+
+        //custom parser
+        juelProperties.setProperty(TreeBuilder.class.getName(), JUELExtensionBuilder.class.getName());
+
+        ExpressionFactory factory = new ExpressionFactoryImpl(juelProperties);
+
+        ExpressionFactoryHolder.setExpressionFactory(factory);
+    }
+
+    public void contextDestroyed(ServletContextEvent contextEvent) {
+    }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELValueStack.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELValueStack.java
new file mode 100644
index 0000000..cf63a79
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELValueStack.java
@@ -0,0 +1,229 @@
+package org.apache.struts2.uelplugin;
+
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.ClearableValueStack;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import org.apache.struts2.uelplugin.elresolvers.AccessorsContextKey;
+
+import javax.el.ELContext;
+import javax.el.ExpressionFactory;
+import javax.el.PropertyNotFoundException;
+import javax.el.ValueExpression;
+import java.util.Map;
+import java.util.TreeMap;
+import java.io.Serializable;
+
+/**
+ * A ValueStack that uses Unified EL as the underlying Expression Language.
+ */
+public class UELValueStack implements ValueStack, ClearableValueStack, Serializable {
+    private static final Logger LOG = LoggerFactory.getLogger(UELValueStack.class);
+
+    private CompoundRoot root = new CompoundRoot();
+    private transient Map context;
+    private Class defaultType;
+    private Map overrides;
+
+    private ELContext elContext;
+    private Container container;
+    private XWorkConverter xworkConverter;
+
+    private boolean logMissingProperties;
+    private boolean devMode;
+
+    public UELValueStack(Container container) {
+        this(container, new CompoundRoot());
+    }
+
+    public UELValueStack(Container container, ValueStack vs) {
+        this(container, new CompoundRoot(vs.getRoot()));
+    }
+
+    public UELValueStack(Container container, CompoundRoot root) {
+        this.container = container;
+        this.xworkConverter = container.getInstance(XWorkConverter.class);
+        setRoot(new CompoundRoot(root));
+    }
+
+    @Inject("devMode")
+    public void setDevMode(String mode) {
+        devMode = "true".equalsIgnoreCase(mode);
+    }
+
+    @Inject(value = "logMissingProperties", required = false)
+    public void setLogMissingProperties(String logMissingProperties) {
+        this.logMissingProperties = "true".equalsIgnoreCase(logMissingProperties);
+    }
+
+    public String findString(String expr, boolean throwException) {
+        return (String) findValue(expr, String.class);
+    }
+
+    public String findString(String expr) {
+        return findString(expr, false);
+    }
+
+    public Object findValue(String expr) {
+        return findValue(expr, Object.class, false);
+    }
+
+    public Object findValue(String expr, boolean throwException) {
+        return findValue(expr, Object.class, throwException);
+    }
+
+    public Object findValue(String expr, Class asType) {
+        return findValue(expr, asType, false);
+    }
+
+    public Object findValue(String expr, Class asType, boolean throwException) {
+        String originalExpression = expr;
+        try {
+            if ((overrides != null) && overrides.containsKey(expr)) {
+                expr = (String) overrides.get(expr);
+            }
+            if (expr != null && expr.startsWith("%{")) {
+                // replace %{ with ${
+                expr = "#" + expr.substring(1);
+            }
+            if (expr != null && !expr.startsWith("${") && !expr.startsWith("#{")) {
+                expr = "#{" + expr + "}";
+            }
+
+            elContext.putContext(AccessorsContextKey.class, context);
+            elContext.putContext(XWorkConverter.class, xworkConverter);
+            elContext.putContext(CompoundRoot.class, root);
+
+            // parse our expression
+            ExpressionFactory factory = getExpressionFactory();
+            ValueExpression valueExpr = factory.createValueExpression(elContext, expr, Object.class);
+            Object retVal = valueExpr.getValue(elContext);
+            if (!Object.class.equals(asType)) {
+                retVal = xworkConverter.convertValue(context, root, null, null, retVal, asType);
+            }
+            return retVal;
+        } catch (Exception e) {
+            return handleException(e, originalExpression, throwException);
+        }
+    }
+
+    private Object handleException(Exception exception, String expression, boolean throwException) {
+        Object ret = context.get(expression);
+
+        if (ret != null)
+            return ret;
+        else {
+            if (exception instanceof PropertyNotFoundException && devMode && logMissingProperties)
+                LOG.warn(exception.getMessage());
+
+            if (throwException)
+                throw new XWorkException(exception);
+            else
+                return null;
+        }
+
+    }
+
+    protected ExpressionFactory getExpressionFactory() {
+        ExpressionFactory factory = ExpressionFactoryHolder.getExpressionFactory();
+        if (factory == null) {
+            String message = "********** FATAL ERROR STARTING UP STRUTS-UEL INTEGRATION **********\n" +
+                    "Looks like the UEL listener was not configured for your web app! \n" +
+                    "Nothing will work until UELServletContextListener is added as a listener in web.xml.\n" +
+                    "You might need to add the following to web.xml: \n" +
+                    "    <listener>\n" +
+                    "        <listener-class>org.apache.struts2.uelplugin.UELServletContextListener</listener-class>\n" +
+                    "    </listener>";
+            LOG.fatal(message);
+            throw new IllegalStateException("Unable to find ExpressionFactory instance. Make sure that 'UELServletContextListener' " +
+                    "is configured in web.xml as a listener");
+        } else
+            return factory;
+    }
+
+    public Map getContext() {
+        return context;
+    }
+
+    public Map getExprOverrides() {
+        return overrides;
+    }
+
+    public CompoundRoot getRoot() {
+        return root;
+    }
+
+    public Object peek() {
+        return root.peek();
+    }
+
+    public Object pop() {
+        return root.pop();
+    }
+
+    public void push(Object o) {
+        root.push(o);
+    }
+
+    public void setDefaultType(Class defaultType) {
+        this.defaultType = defaultType;
+    }
+
+    public void setExprOverrides(Map overrides) {
+        if (this.overrides == null) {
+            this.overrides = overrides;
+        } else {
+            this.overrides.putAll(overrides);
+        }
+    }
+
+    public void set(String key, Object o) {
+        overrides.put(key, o);
+    }
+
+    public void setValue(String expr, Object value) {
+        setValue(expr, value, false);
+    }
+
+    public void setValue(String expr, Object value, boolean throwExceptionOnFailure) {
+        try {
+            if (expr != null && !expr.startsWith("${") && !expr.startsWith("#{")) {
+                expr = "#{" + expr + "}";
+            }
+            elContext.putContext(AccessorsContextKey.class, context);
+            elContext.putContext(XWorkConverter.class, xworkConverter);
+            elContext.putContext(CompoundRoot.class, root);
+
+            // parse our expression
+            ExpressionFactory factory = getExpressionFactory();
+            ValueExpression valueExpr = factory.createValueExpression(elContext, expr, Object.class);
+            valueExpr.setValue(elContext, value);
+        } catch (Exception e) {
+            if (e instanceof PropertyNotFoundException && devMode && logMissingProperties)
+                LOG.warn("Could not find property [" + ((PropertyNotFoundException) e).getMessage() + "]");
+
+            if (throwExceptionOnFailure)
+                throw new XWorkException(e);
+        }
+    }
+
+    public int size() {
+        return root.size();
+    }
+
+    protected void setRoot(CompoundRoot root) {
+        this.context = new TreeMap();
+        context.put(VALUE_STACK, this);
+        this.root = root;
+        elContext = new CompoundRootELContext(container);
+    }
+
+    public void clearContextValues() {
+        getContext().clear();
+    }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELValueStackFactory.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELValueStackFactory.java
new file mode 100644
index 0000000..7272694
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UELValueStackFactory.java
@@ -0,0 +1,45 @@
+package org.apache.struts2.uelplugin;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ValueStackFactory;
+
+import javax.el.ExpressionFactory;
+
+/**
+ * Creates UelValueStacks.
+ */
+public class UELValueStackFactory implements ValueStackFactory {
+    private ExpressionFactory factory;
+
+    private XWorkConverter xworkConverter;
+
+    private Container container;
+
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+
+    @Inject
+    public void setContainer(Container container) throws ClassNotFoundException {
+        this.container = container;
+    }
+
+
+    public ValueStack createValueStack() {
+        ValueStack results = new UELValueStack(container);
+        results.getContext().put(ActionContext.CONTAINER, container);
+        return results;
+    }
+
+    public ValueStack createValueStack(ValueStack stack) {
+        ValueStack results = new UELValueStack(container, stack);
+        container.inject(results);
+        results.getContext().put(ActionContext.CONTAINER, container);
+        return results;
+    }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/CompoundRootELResolver.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/CompoundRootELResolver.java
index 032eb6a..0e9c3e2 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/CompoundRootELResolver.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/CompoundRootELResolver.java
@@ -1,7 +1,6 @@
 package org.apache.struts2.uelplugin.elresolvers;
 
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
-import com.opensymphony.xwork2.conversion.NullHandler;
 import com.opensymphony.xwork2.util.CompoundRoot;
 import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
 import com.opensymphony.xwork2.inject.Container;
@@ -10,14 +9,7 @@
 import org.apache.commons.lang.xwork.StringUtils;
 
 import javax.el.ELContext;
-import javax.el.ELResolver;
-import java.beans.BeanInfo;
-import java.beans.FeatureDescriptor;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
 import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.Map;
 
 /**
@@ -31,8 +23,8 @@
     }
 
     @Override
-    public Object getValue(ELContext context, Object base, Object property) {
-        if (context == null) {
+    public Object getValue(ELContext elContext, Object base, Object property) {
+        if (elContext == null) {
             throw new IllegalArgumentException("ElContext cannot be null");
         }
 
@@ -46,16 +38,17 @@
             return null;
         }
 
-        CompoundRoot root = (CompoundRoot) context.getContext(CompoundRoot.class);
+        CompoundRoot root = (CompoundRoot) elContext.getContext(CompoundRoot.class);
         if (root == null) {
             return null;
         }
 
         if ("top".equals(propertyName) && root.size() > 0) {
+            elContext.setPropertyResolved(true);
             return root.get(0);
         }
 
-        Map<String, Object> reflectionContext = (Map) context.getContext(AccessorsContextKey.class);
+        Map<String, Object> reflectionContext = (Map) elContext.getContext(AccessorsContextKey.class);
 
         Object bean = findObjectForProperty(root, propertyName);
         if (bean != null) {
@@ -70,10 +63,10 @@
                 reflectionProvider.setValue(propertyName, reflectionContext, bean, retVal);
             }
 
-            context.setPropertyResolved(true);
+            elContext.setPropertyResolved(true);
             return retVal;
         }
-        
+
         return null;
     }
 
@@ -93,15 +86,20 @@
         }
 
         CompoundRoot root = (CompoundRoot) context.getContext(CompoundRoot.class);
+        Map<String, Object> reflectionContext = (Map) context.getContext(AccessorsContextKey.class);
         String propertyName = (String) property;
         try {
             if (base == null && property != null && root != null) {
                 Object bean = findObjectForProperty(root, propertyName);
                 if (bean != null) {
+                    reflectionContext.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, bean.getClass());
+                    reflectionContext.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, propertyName);
+
+
                     XWorkConverter converter = (XWorkConverter) context.getContext(XWorkConverter.class);
                     if (converter != null && root != null) {
                         Class propType = determineType(bean, propertyName);
-                        value = converter.convertValue(null, value, propType);
+                        value = converter.convertValue(reflectionContext, bean, null, propertyName, value, propType);
                     }
                     BeanUtils.setProperty(bean, propertyName, value);
                     context.setPropertyResolved(true);
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkBeanELResolver.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkBeanELResolver.java
index daedd79..ef0409e 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkBeanELResolver.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkBeanELResolver.java
@@ -18,14 +18,15 @@
         if (target != null && property != null) {

             Map<String, Object> reflectionContext = (Map<String, Object>) elContext.getContext(AccessorsContextKey.class);

             String propertyName = property.toString();

+            Class targetType = target.getClass();

 

             //only handle this if there is such a property

             if (PropertyUtils.isReadable(target, propertyName)) {

                 try {

                     Object obj = reflectionProvider.getValue(propertyName, reflectionContext, target);

 

-                    reflectionContext.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, target.getClass());

-                    reflectionContext.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, property.toString());

+                    reflectionContext.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, targetType);

+                    reflectionContext.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, propertyName);

 

                     //if object is null, and create objects is enabled, lets do it

                     if (obj == null && ReflectionContextState.isCreatingNullObjects(reflectionContext)) {

diff --git a/struts2-uel-plugin/src/main/resources/struts-plugin.xml b/struts2-uel-plugin/src/main/resources/struts-plugin.xml
index 89314f6..b684e5c 100644
--- a/struts2-uel-plugin/src/main/resources/struts-plugin.xml
+++ b/struts2-uel-plugin/src/main/resources/struts-plugin.xml
@@ -6,11 +6,11 @@
 
 <struts>
     <bean type="com.opensymphony.xwork2.util.ValueStackFactory" name="uel"
-          class="org.apache.struts2.uelplugin.UelValueStackFactory"/>
+          class="org.apache.struts2.uelplugin.UELValueStackFactory"/>
     <bean type="com.opensymphony.xwork2.util.reflection.ReflectionProvider" name="uel"
           class="org.apache.struts2.uelplugin.reflection.GenericReflectionProvider"/>
     <bean type="com.opensymphony.xwork2.util.reflection.ReflectionContextFactory" name="uel"
-          class="org.apache.struts2.uelplugin.UelReflectionContextFactory"/>
+          class="org.apache.struts2.uelplugin.UELReflectionContextFactory"/>
 
     <constant name="struts.valueStackFactory" value="uel"/>
     <constant name="struts.reflectionProvider" value="uel"/>
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUelBaseTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUELTest.java
similarity index 79%
rename from struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUelBaseTest.java
rename to struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUELTest.java
index 185803c..6b98b38 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUelBaseTest.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUELTest.java
@@ -2,19 +2,12 @@
 
 import com.opensymphony.xwork2.XWorkTestCase;
 import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.ActionProxyFactory;
 import com.opensymphony.xwork2.inject.ContainerBuilder;
-import com.opensymphony.xwork2.inject.Factory;
-import com.opensymphony.xwork2.inject.Context;
-import com.opensymphony.xwork2.inject.Scope;
 import com.opensymphony.xwork2.test.StubConfigurationProvider;
-import com.opensymphony.xwork2.config.ConfigurationManager;
-import com.opensymphony.xwork2.config.Configuration;
 import com.opensymphony.xwork2.config.ConfigurationException;
 import com.opensymphony.xwork2.util.CompoundRoot;
 import com.opensymphony.xwork2.util.ValueStack;
 import com.opensymphony.xwork2.util.ValueStackFactory;
-import com.opensymphony.xwork2.util.LocalizedTextUtil;
 import com.opensymphony.xwork2.util.location.LocatableProperties;
 import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
@@ -22,7 +15,6 @@
 import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.uelplugin.reflection.GenericReflectionProvider;
 import org.apache.struts2.util.StrutsTypeConverter;
-import org.apache.commons.beanutils.converters.DateConverter;
 
 import javax.servlet.ServletContextEvent;
 import javax.el.ExpressionFactory;
@@ -31,11 +23,11 @@
 import java.text.DateFormat;
 
 
-public abstract class AbstractUelBaseTest extends XWorkTestCase {
+public abstract class AbstractUELTest extends XWorkTestCase {
     private ExpressionFactory factory = ExpressionFactory.newInstance();
     protected XWorkConverter converter;
     protected CompoundRoot root;
-    protected UelValueStack stack;
+    protected UELValueStack stack;
     protected DateFormat format = DateFormat.getDateInstance();
     protected ReflectionProvider reflectionProvider;
 
@@ -63,8 +55,8 @@
 
         loadConfigurationProviders(new StubConfigurationProvider() {
             public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
-                builder.factory(ValueStack.class, UelValueStack.class);
-                builder.factory(ValueStackFactory.class, UelValueStackFactory.class);
+                builder.factory(ValueStack.class, UELValueStack.class);
+                builder.factory(ValueStackFactory.class, UELValueStackFactory.class);
                 builder.factory(ReflectionProvider.class, GenericReflectionProvider.class);
                 //builder.factory(StrutsTypeConverter)
             }
@@ -74,7 +66,7 @@
         reflectionProvider = container.getInstance(ReflectionProvider.class);
         converter.registerConverter("java.util.Date", new DateConverter());
         this.root = new CompoundRoot();
-        this.stack = new UelValueStack(container);
+        this.stack = new UELValueStack(container);
         stack.setRoot(root);
         stack.getContext().put(ActionContext.CONTAINER, container);
 
@@ -84,7 +76,7 @@
         ServletActionContext.setServletContext(servletContext);
 
         //simulate start up
-        UelServletContextListener listener = new UelServletContextListener();
+        UELServletContextListener listener = new UELServletContextListener();
         listener.contextInitialized(new ServletContextEvent(servletContext));
     }
 }
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/BuiltinFunctionsTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/BuiltinFunctionsTest.java
index 9be211f..c48ecf4 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/BuiltinFunctionsTest.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/BuiltinFunctionsTest.java
@@ -1,21 +1,11 @@
 package org.apache.struts2.uelplugin;
 
-import com.opensymphony.xwork2.XWorkTestCase;
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.util.CompoundRoot;
-import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
-
 import java.lang.reflect.InvocationTargetException;
 
-import org.springframework.mock.web.MockServletContext;
-import org.apache.struts2.ServletActionContext;
 import org.apache.struts2.views.util.ContextUtil;
-import org.apache.struts2.uelplugin.UelServletContextListener;
-
-import javax.servlet.ServletContextEvent;
 
 
-public class BuiltinFunctionsTest extends AbstractUelBaseTest {
+public class BuiltinFunctionsTest extends AbstractUELTest {
     public void testGetText() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
         TestAction action = new TestAction();
         stack.push(action);
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ChildTestAction.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ChildTestAction.java
new file mode 100644
index 0000000..ca55152
--- /dev/null
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ChildTestAction.java
@@ -0,0 +1,4 @@
+package org.apache.struts2.uelplugin;

+

+public class ChildTestAction extends TestAction {

+}

diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ParametersTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ParametersTest.java
deleted file mode 100644
index 1868ce7..0000000
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ParametersTest.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package org.apache.struts2.uelplugin;
-
-import com.opensymphony.xwork2.interceptor.ParametersInterceptor;
-import com.opensymphony.xwork2.ActionProxy;
-import com.opensymphony.xwork2.Action;
-import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
-
-import java.util.Map;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.lang.reflect.InvocationTargetException;
-
-
-public class ParametersTest extends AbstractUelBaseTest {
-    public void testWriteList() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        //not null
-        List list = new ArrayList();
-        TestObject obj = new TestObject();
-        obj.setList(list);
-        assertNotNull(obj.getList());
-        root.push(obj);
-
-        stack.setValue("list[0]", "val");
-        assertEquals(1, list.size());
-        assertEquals("val", list.get(0));
-
-        //null list
-        obj.setList(null);
-        assertNull(obj.getList());
-        stack.setValue("list[0]", "val");
-        assertNotNull(obj.getList());
-        assertEquals("val", stack.findValue("list[0]"));
-
-        //test out of index
-        obj.setList(null);
-        stack.setValue("list[3]", "val");
-        assertEquals(4, obj.getList().size());
-        assertEquals("val", obj.getList().get(3));
-
-        //test type determiner
-        obj.setTypedList(null);
-        stack.setValue("typedList[1].value", "val");
-        assertEquals(2, obj.getTypedList().size());
-        assertEquals("val", obj.getTypedList().get(1).getValue());
-    }
-
-     public void testWriteArray() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        //not null
-        Object[] array = new Object[2];
-        TestObject obj = new TestObject();
-        //obj.setObjectArray(array);
-        //assertNotNull(obj.getObjectArray());
-        root.push(obj);
-
-        stack.setValue("objectArray[0].value", "val");
-        assertEquals("val", ((TestObject)array[0]).getValue());
-    }
-
-    public void testWriteMap() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        //not null
-        Map map = new HashMap();
-        TestObject obj = new TestObject();
-        obj.setMap(map);
-        assertNotNull(obj.getMap());
-        root.push(obj);
-
-        stack.setValue("map['str']", "val");
-        assertEquals(1, map.size());
-        assertEquals("val", map.get("str"));
-
-        //null list
-        obj.setMap(null);
-        assertNull(obj.getMap());
-        stack.setValue("map['str']", "val");
-        assertNotNull(obj.getMap());
-        assertEquals("val", stack.findValue("map['str']"));
-
-        //test type determiner
-        obj.setTypedMap(null);
-        stack.setValue("typedMap[1].value", "val");
-        assertEquals(1, obj.getTypedMap().size());
-        assertEquals("val", obj.getTypedMap().get(1).getValue());
-    }
-
-    public void testSetPropertiesOnNestedNullObject() {
-        TestObject obj = new TestObject();
-        assertNull(obj.getInner());
-        root.push(obj);
-
-        //inner is null, it will be catched bye the CompoundRoolELResolver
-        stack.setValue("inner.value", "val");
-        assertNotNull(obj.getInner());
-        assertEquals("val", obj.getInner().getValue());
-
-
-        //second nested property null
-        stack.setValue("inner.inner.value", "val");
-        assertNotNull(obj.getInner().getInner());
-        assertEquals("val", obj.getInner().getInner().getValue());
-    }
-
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        Map context = stack.getContext();
-
-        ReflectionContextState.setCreatingNullObjects(context, true);
-        ReflectionContextState.setDenyMethodExecution(context, true);
-        ReflectionContextState.setReportingConversionErrors(context, true);
-    }
-}
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestAction.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestAction.java
index 03b06de..3e3f493 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestAction.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestAction.java
@@ -8,10 +8,18 @@
 @Conversion
 public class TestAction extends ActionSupport {
     private TestObject object;
+    private int bar;
+
+    public int getBar() {
+        return bar;
+    }
+
+    public void setBar(int bar) {
+        this.bar = bar;
+    }
 
     private String converted;
 
-    @TypeConversion(type = ConversionType.APPLICATION, converter = "org.apache.struts2.uelplugin.DummyTypeConverter")
     public String getConverted() {
         return converted;
     }
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestObject.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestObject.java
index 9d31e45..d87b8b8 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestObject.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/TestObject.java
@@ -18,9 +18,32 @@
     private Map map;
     private Map<Integer, TestObject> typedMap;
     private Object[] objectArray;
-    private Integer[] typedArray;
+    private int[] typedArray;
+    private TestObject[] typedArray2;
     private Set set;
 
+    private ChildTestAction childTestAction;
+
+    public ChildTestAction getChildTestAction() {
+        return childTestAction;
+    }
+
+    public void setChildTestAction(ChildTestAction childTestAction) {
+        this.childTestAction = childTestAction;
+    }
+
+    public Object getFail() {
+        throw new RuntimeException("kaboom");
+    }
+
+    public TestObject[] getTypedArray2() {
+        return typedArray2;
+    }
+
+    public void setTypedArray2(TestObject[] typedArray2) {
+        this.typedArray2 = typedArray2;
+    }
+
     public Set getSet() {
         return set;
     }
@@ -37,11 +60,11 @@
         this.objectArray = objectArray;
     }
 
-    public Integer[] getTypedArray() {
+    public int[] getTypedArray() {
         return typedArray;
     }
 
-    public void setTypedArray(Integer[] typedArray) {
+    public void setTypedArray(int[] typedArray) {
         this.typedArray = typedArray;
     }
 
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/JuelMethodInvocationTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELMethodInvocationTest.java
similarity index 65%
rename from struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/JuelMethodInvocationTest.java
rename to struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELMethodInvocationTest.java
index 9ed8fb0..3026b3d 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/JuelMethodInvocationTest.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELMethodInvocationTest.java
@@ -1,19 +1,9 @@
 package org.apache.struts2.uelplugin;
 
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.XWorkTestCase;
-import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
-import com.opensymphony.xwork2.util.CompoundRoot;
-import org.apache.struts2.ServletActionContext;
-import org.apache.struts2.uelplugin.UelServletContextListener;
-import org.springframework.mock.web.MockServletContext;
-
-import javax.servlet.ServletContextEvent;
 import java.lang.reflect.InvocationTargetException;
-import java.util.HashMap;
 
 
-public class JuelMethodInvocationTest extends AbstractUelBaseTest {
+public class UELMethodInvocationTest extends AbstractUELTest {
     public void testBasicMethods() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
         assertEquals("text", stack.findValue("${' text '.trim()}"));
         assertEquals(3, stack.findValue("${'123'.length()}"));
@@ -29,4 +19,14 @@
         stack.getContext().put("s1", "Luthor");
         assertEquals("Lex Luthor", stack.findValue("${#s0.concat(' ').concat(#s1)}"));
     }
+
+     public void testCallMethodsOnCompundRoot() {
+        //this shuld not fail as the property is defined on a parent class
+        TestObject obj = new TestObject();
+        root.push(obj);
+        ChildTestAction childTestAction = new ChildTestAction();
+        obj.setChildTestAction(childTestAction);
+
+        assertSame(childTestAction, stack.findValue("top.getChildTestAction()", true));
+    }
 }
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UelTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELStackReadValueTest.java
similarity index 67%
rename from struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UelTest.java
rename to struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELStackReadValueTest.java
index 2394bf1..9e1bcc7 100644
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UelTest.java
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELStackReadValueTest.java
@@ -1,36 +1,21 @@
 package org.apache.struts2.uelplugin;
 
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.XWorkTestCase;
-import com.opensymphony.xwork2.ActionProxyFactory;
-import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
-import com.opensymphony.xwork2.inject.Container;
-import com.opensymphony.xwork2.inject.ContainerBuilder;
-import com.opensymphony.xwork2.inject.Scope;
-import com.opensymphony.xwork2.config.ConfigurationManager;
-import com.opensymphony.xwork2.config.Configuration;
-import com.opensymphony.xwork2.config.ConfigurationProvider;
-import com.opensymphony.xwork2.config.ConfigurationException;
-import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
-import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
-import com.opensymphony.xwork2.util.location.LocatableProperties;
-import com.opensymphony.xwork2.util.ValueStack;
-import com.opensymphony.xwork2.util.ValueStackFactory;
-import com.opensymphony.xwork2.util.LocalizedTextUtil;
-import com.opensymphony.xwork2.util.CompoundRoot;
-import org.apache.struts2.ServletActionContext;
-import org.apache.struts2.uelplugin.UelServletContextListener;
-import org.apache.struts2.util.StrutsTypeConverter;
-import org.springframework.mock.web.MockServletContext;
-
-import javax.el.ExpressionFactory;
-import javax.servlet.ServletContextEvent;
 import java.lang.reflect.InvocationTargetException;
-import java.text.DateFormat;
-import java.text.ParseException;
 import java.util.*;
 
-public class UelTest extends AbstractUelBaseTest {
+public class UELStackReadValueTest extends AbstractUELTest {
+
+    public void testTop() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        TestObject obj = new TestObject();
+        obj.setValue("0");
+        root.push(obj);
+        
+        obj = new TestObject();
+        obj.setValue("1");
+        root.push(obj);
+
+        assertEquals("1", stack.findValue("top.value"));
+    }
 
     public void testReadList() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
         TestObject obj = new TestObject();
@@ -45,7 +30,7 @@
 
     public void testReadArray() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
         TestObject obj = new TestObject();
-        Integer[] ints = {10, 20};
+        int[] ints = {10, 20};
         obj.setTypedArray(ints);
         root.push(obj);
 
@@ -54,7 +39,7 @@
     }
 
 
-    public void testMap() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+    public void testReadMap() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
         HashMap map = new HashMap();
         map.put("nameValue", "Lex");
         TestObject obj = new TestObject();
@@ -183,18 +168,6 @@
         assertEquals(100, stack.findValue("${inner.age}"));
     }
 
-    public void testSetStringArray() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        TestObject obj = new TestObject();
-        root.add(obj);
-
-        stack.setValue("${value}", new String[]{"Hello World"});
-        String value = stack.findString("${value}");
-        assertEquals("Hello World", value);
-
-        stack.setValue("${age}", new String[]{"67"});
-        assertEquals(new Integer(67), stack.findValue("${age}"));
-    }
-
     public void testDeferredFind() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
         TestObject obj = new TestObject();
         root.add(obj);
@@ -211,35 +184,6 @@
         assertEquals(stack.findString("#{date}"), format.format(obj.getDate()));
     }
 
-    public void test2LevelSet() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        TestObject obj = new TestObject();
-        TestObject nestedObj = new TestObject();
-        obj.setInner(nestedObj);
-        root.add(obj);
-
-        stack.setValue("${inner.age}", "66");
-        assertEquals(66, obj.getInner().getAge());
-    }
-
-    public void testTypeConversion() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
-        TestObject obj = new TestObject();
-        TestObject inner = new TestObject();
-        obj.setInner(inner);
-        root.add(obj);
-
-        stack.setValue("${age}", "22");
-        assertEquals(stack.findValue("${age}"), obj.getAge());
-
-        stack.setValue("${inner.value}", "George");
-        assertEquals(stack.findValue("${inner.value}"), obj.getInner().getValue());
-
-        stack.setValue("${inner.age}", "44");
-        assertEquals(stack.findValue("${inner.age}"), obj.getInner().getAge());
-
-        stack.setValue("${date}", new Date());
-        assertEquals(stack.findString("${date}"), format.format(obj.getDate()));
-    }
-
     public void testNotFound() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
         TestObject obj = new TestObject();
         root.add(obj);
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELStackSetValueTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELStackSetValueTest.java
new file mode 100644
index 0000000..a0e43e6
--- /dev/null
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELStackSetValueTest.java
@@ -0,0 +1,159 @@
+package org.apache.struts2.uelplugin;
+
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+
+import java.util.*;
+import java.lang.reflect.InvocationTargetException;
+
+
+public class UELStackSetValueTest extends AbstractUELTest {
+    public void testSuperNested() {
+        TestObject obj = new TestObject();
+        root.push(obj);
+
+        stack.setValue("inner.typedMap[10].inner.typedList[2].typedMap[1].value", "whoa");
+        assertEquals("whoa", obj.getInner().getTypedMap().get(10).getInner().getTypedList().get(2).getTypedMap().get(1).getValue());
+    }
+
+
+    public void testWriteList() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        //not null
+        List list = new ArrayList();
+        TestObject obj = new TestObject();
+        obj.setList(list);
+        assertNotNull(obj.getList());
+        root.push(obj);
+
+        stack.setValue("list[0]", "val");
+        assertEquals(1, list.size());
+        assertEquals("val", list.get(0));
+
+        //null list
+        obj.setList(null);
+        assertNull(obj.getList());
+        stack.setValue("list[0]", "val");
+        assertNotNull(obj.getList());
+        assertEquals("val", stack.findValue("list[0]"));
+
+        //test out of index
+        obj.setList(null);
+        stack.setValue("list[3]", "val");
+        assertEquals(4, obj.getList().size());
+        assertEquals("val", obj.getList().get(3));
+
+        //test type determiner
+        obj.setTypedList(null);
+        stack.setValue("typedList[1].value", "val");
+        assertEquals(2, obj.getTypedList().size());
+        assertEquals("val", obj.getTypedList().get(1).getValue());
+    }
+
+    public void testWriteArray() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        //not null
+        TestObject[] array = new TestObject[2];
+        TestObject obj = new TestObject();
+        obj.setTypedArray2(array);
+        assertNotNull(obj.getTypedArray2());
+        root.push(obj);
+
+        stack.setValue("typedArray2[0].value", "val");
+        assertNotNull(array[0]);
+        assertEquals("val", ((TestObject) array[0]).getValue());
+    }
+
+    public void testWriteMap() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        //not null
+        Map map = new HashMap();
+        TestObject obj = new TestObject();
+        obj.setMap(map);
+        assertNotNull(obj.getMap());
+        root.push(obj);
+
+        stack.setValue("map['str']", "val");
+        assertEquals(1, map.size());
+        assertEquals("val", map.get("str"));
+
+        //null list
+        obj.setMap(null);
+        assertNull(obj.getMap());
+        stack.setValue("map['str']", "val");
+        assertNotNull(obj.getMap());
+        assertEquals("val", stack.findValue("map['str']"));
+
+        //test type determiner
+        obj.setTypedMap(null);
+        stack.setValue("typedMap[1].value", "val");
+        assertEquals(1, obj.getTypedMap().size());
+        assertEquals("val", obj.getTypedMap().get(1).getValue());
+    }
+
+    public void testSetPropertiesOnNestedNullObject() {
+        TestObject obj = new TestObject();
+        assertNull(obj.getInner());
+        root.push(obj);
+
+        //inner is null, it will be catched bye the CompoundRoolELResolver
+        stack.setValue("inner.value", "val");
+        assertNotNull(obj.getInner());
+        assertEquals("val", obj.getInner().getValue());
+
+
+        //second nested property null
+        stack.setValue("inner.inner.value", "val");
+        assertNotNull(obj.getInner().getInner());
+        assertEquals("val", obj.getInner().getInner().getValue());
+    }
+
+    public void testSetStringArray() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        TestObject obj = new TestObject();
+        root.add(obj);
+
+        stack.setValue("${value}", new String[]{"Hello World"});
+        String value = stack.findString("${value}");
+        assertEquals("Hello World", value);
+
+        stack.setValue("${age}", new String[]{"67"});
+        assertEquals(new Integer(67), stack.findValue("${age}"));
+    }
+
+    public void test2LevelSet() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        TestObject obj = new TestObject();
+        TestObject nestedObj = new TestObject();
+        obj.setInner(nestedObj);
+        root.add(obj);
+
+        stack.setValue("${inner.age}", "66");
+        assertEquals(66, obj.getInner().getAge());
+    }
+
+    public void testTypeConversion() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+        TestObject obj = new TestObject();
+        TestObject inner = new TestObject();
+        obj.setInner(inner);
+        root.add(obj);
+
+        stack.setValue("${age}", "22");
+        assertEquals(stack.findValue("${age}"), obj.getAge());
+
+        stack.setValue("${inner.value}", "George");
+        assertEquals(stack.findValue("${inner.value}"), obj.getInner().getValue());
+
+        stack.setValue("${inner.age}", "44");
+        assertEquals(stack.findValue("${inner.age}"), obj.getInner().getAge());
+
+        stack.setValue("${date}", new Date());
+        assertEquals(stack.findString("${date}"), format.format(obj.getDate()));
+    }
+
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Map context = stack.getContext();
+
+        ReflectionContextState.setCreatingNullObjects(context, true);
+        ReflectionContextState.setDenyMethodExecution(context, true);
+        ReflectionContextState.setReportingConversionErrors(context, true);
+    }
+}
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELValueStackOtherTests.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELValueStackOtherTests.java
new file mode 100644
index 0000000..a4af15d
--- /dev/null
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/UELValueStackOtherTests.java
@@ -0,0 +1,159 @@
+package org.apache.struts2.uelplugin;

+

+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;

+import com.opensymphony.xwork2.ActionContext;

+

+import java.util.Map;

+import java.util.LinkedHashMap;

+

+

+public class UELValueStackOtherTests extends AbstractUELTest {

+

+    public void testExpOverridesCanStackExpUp() throws Exception {

+        Map expr1 = new LinkedHashMap();

+        expr1.put("expr1", "'expr1value'");

+

+        stack.setExprOverrides(expr1);

+

+        assertEquals(stack.findValue("expr1"), "expr1value");

+

+        Map expr2 = new LinkedHashMap();

+        expr2.put("expr2", "'expr2value'");

+        expr2.put("expr3", "'expr3value'");

+        stack.setExprOverrides(expr2);

+

+        assertEquals(stack.findValue("expr2"), "expr2value");

+        assertEquals(stack.findValue("expr3"), "expr3value");

+    }

+

+    public void testArrayAsString() {

+        TestObject obj = new TestObject();

+        obj.setTypedArray(new int[]{1, 2});

+        root.push(obj);

+

+        assertEquals("1, 2", stack.findValue("typedArray", String.class));

+    }

+

+    public void testFailsOnExceptionWithThrowException() {

+        TestObject obj = new TestObject();

+        root.push(obj);

+        try {

+            stack.findValue("fail", true);

+            fail("Failed to throw exception on EL error");

+        } catch (Exception ex) {

+            //ok

+        }

+    }

+

+    public void testFailsOnMissingPropertyWithThrowException() {

+        TestObject obj = new TestObject();

+        root.push(obj);

+        try {

+            stack.findValue("someprop12", true);

+            fail("Failed to throw exception on EL error");

+        } catch (Exception ex) {

+            //ok

+        }

+    }

+

+     public void testFailsOnMissingNestedPropertyWithThrowException() {

+        TestObject obj = new TestObject();

+        root.push(obj);

+        try {

+            stack.findValue("top.someprop12", true);

+            fail("Failed to throw exception on EL error");

+        } catch (Exception ex) {

+            //ok

+        }

+    }

+

+    public void testFailsOnMissingMethodWithThrowException() {

+        TestObject obj = new TestObject();

+        root.push(obj);

+        try {

+            stack.findValue("top.somethingweird()", true);

+            fail("Failed to throw exception on EL error");

+        } catch (Exception ex) {

+            //ok

+        }

+    }

+

+    public void testDoesNotFailOnExceptionWithoutThrowException() {

+        TestObject obj = new TestObject();

+        root.push(obj);

+        stack.findValue("fail", false);

+        stack.findValue("fail");

+    }

+

+    public void testDoesNotFailOnInheritedPropertiesWithThrowException() {

+        //this shuld not fail as the property is defined on a parent class

+        TestObject obj = new TestObject();

+        root.push(obj);

+        ChildTestAction childTestAction = new ChildTestAction();

+        obj.setChildTestAction(childTestAction);

+

+        assertNull(childTestAction.getConverted());

+        stack.findValue("childTestAction.converted", true);

+    }

+

+    public void testDoesNotFailOnInheritedMethodsWithThrowException() {

+        //this shuld not fail as the property is defined on a parent class

+        TestObject obj = new TestObject();

+        root.push(obj);

+        ChildTestAction childTestAction = new ChildTestAction();

+        obj.setChildTestAction(childTestAction);

+

+        assertNull(childTestAction.getConverted());

+        stack.findValue("top.getChildTestAction().converted", true);

+    }

+

+    public void testFailsOnInheritedMethodsWithThrowException() {

+        //this shuld not fail as the property is defined on a parent class

+        TestObject obj = new TestObject();

+        root.push(obj);

+        ChildTestAction childTestAction = new ChildTestAction();

+        obj.setChildTestAction(childTestAction);

+

+        assertNull(childTestAction.getConverted());

+

+        try {

+            stack.findValue("top.getChildTestAction().converted2", true);

+            fail("should have failed because of missing property");

+        } catch (Exception e) {

+        }

+    }

+

+     public void testPrimitiveSettingWithInvalidValueAddsFieldErrorInNonDevMode() {

+        TestAction action = new TestAction();

+        stack.getContext().put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.TRUE);

+        stack.setDevMode("false");

+        stack.push(action);

+        stack.setValue("bar", "3x");

+

+        Map conversionErrors = (Map) stack.getContext().get(ActionContext.CONVERSION_ERRORS);

+        assertTrue(conversionErrors.containsKey("bar"));

+    }

+

+     public void testPrimitiveSettingWithInvalidValueAddsFieldErrorInDevMode() {

+        TestAction action = new TestAction();

+        stack.getContext().put(XWorkConverter.REPORT_CONVERSION_ERRORS, Boolean.TRUE);

+        stack.setDevMode("true");

+        stack.push(action);

+        stack.setValue("bar", "3x");

+

+        Map conversionErrors = (Map) stack.getContext().get(ActionContext.CONVERSION_ERRORS);

+        assertTrue(conversionErrors.containsKey("bar"));

+    }

+

+    public void testObjectSettingWithInvalidValueDoesNotCauseSetCalledWithNull() {

+        TestObject obj = new TestObject();

+        root.push(obj);

+        ChildTestAction obj2 = new ChildTestAction();

+        obj.setChildTestAction(obj2);

+

+        stack.setValue("childTestAction", "whoa");

+        assertNotNull(obj.getChildTestAction());

+        assertSame(obj2, obj.getChildTestAction());

+    }

+

+}