Anonymous avatar Anonymous committed d628f99

initial import (requires mvel trunk build)

git-svn-id: http://svn.opensymphony.com/svn/xwork/branches/xwork-mvel@1907 e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (19)

             <version>1.1.1</version>
             <optional>true</optional>
         </dependency>
+         <dependency>
+            <groupId>org.mvel</groupId>
+            <artifactId>mvel2</artifactId>
+            <version>2.0.6-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>opensymphony</groupId>
             <artifactId>ognl</artifactId>

src/java/com/opensymphony/xwork2/SecurityMemberAccess.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2;
+
+import ognl.DefaultMemberAccess;
+
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Allows access decisions to be made on the basis of whether a member is static or not.
+ * Also blocks or allows access to properties.
+ */
+public class SecurityMemberAccess extends DefaultMemberAccess {
+
+    private boolean allowStaticMethodAccess;
+    Set<Pattern> excludeProperties = Collections.emptySet();
+    Set<Pattern> acceptProperties = Collections.emptySet();
+
+    public SecurityMemberAccess(boolean method) {
+        super(false);
+        allowStaticMethodAccess = method;
+    }
+
+    public boolean getAllowStaticMethodAccess() {
+        return allowStaticMethodAccess;
+    }
+
+    public void setAllowStaticMethodAccess(boolean allowStaticMethodAccess) {
+        this.allowStaticMethodAccess = allowStaticMethodAccess;
+    }
+
+    @Override
+    public boolean isAccessible(Map context, Object target, Member member,
+                                String propertyName) {
+
+        boolean allow = true;
+        int modifiers = member.getModifiers();
+        if (Modifier.isStatic(modifiers)) {
+            if (member instanceof Method && !getAllowStaticMethodAccess()) {
+                allow = false;
+                if (target instanceof Class) {
+                    Class clazz = (Class) target;
+                    Method method = (Method) member;
+                    if (Enum.class.isAssignableFrom(clazz) && method.getName().equals("values"))
+                        allow = true;
+                }
+            }
+        }
+
+        //failed static test
+        if (!allow)
+            return false;
+
+        // Now check for standard scope rules
+        if (!super.isAccessible(context, target, member, propertyName))
+            return false;
+
+        return isAcceptableProperty(propertyName);
+    }
+
+    protected boolean isAcceptableProperty(String name) {
+        if (isAccepted(name) && !isExcluded(name)) {
+            return true;
+        }
+        return false;
+    }
+
+    protected boolean isAccepted(String paramName) {
+        if (!this.acceptProperties.isEmpty()) {
+            for (Pattern pattern : acceptProperties) {
+                Matcher matcher = pattern.matcher(paramName);
+                if (matcher.matches()) {
+                    return true;
+                }
+            }
+
+            //no match, but acceptedParams is not empty
+            return false;
+        }
+
+        //empty acceptedParams
+        return true;
+    }
+
+    protected boolean isExcluded(String paramName) {
+        if (!this.excludeProperties.isEmpty()) {
+            for (Pattern pattern : excludeProperties) {
+                Matcher matcher = pattern.matcher(paramName);
+                if (matcher.matches()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void setExcludeProperties(Set<Pattern> excludeProperties) {
+        this.excludeProperties = excludeProperties;
+    }
+
+    public void setAcceptProperties(Set<Pattern> acceptedProperties) {
+        this.acceptProperties = acceptedProperties;
+    }
+}

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

 import com.opensymphony.xwork2.ActionProxyFactory;
 import com.opensymphony.xwork2.DefaultActionProxyFactory;
 import com.opensymphony.xwork2.DefaultTextProvider;
-import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
 import com.opensymphony.xwork2.TextProvider;
-import com.opensymphony.xwork2.UnknownHandlerManager;
 import com.opensymphony.xwork2.config.Configuration;
 import com.opensymphony.xwork2.config.ConfigurationException;
 import com.opensymphony.xwork2.config.ConfigurationProvider;
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
 import com.opensymphony.xwork2.inject.ContainerBuilder;
 import com.opensymphony.xwork2.inject.Scope;
+import com.opensymphony.xwork2.mvel.MVELReflectionProvider;
+import com.opensymphony.xwork2.mvel.MVELUtil;
+import com.opensymphony.xwork2.mvel.accessors.MVELGetListener;
+import com.opensymphony.xwork2.mvel.accessors.MVELListPropertyAccessor;
+import com.opensymphony.xwork2.mvel.accessors.MVELNullPropertyHandlerWrapper;
 import com.opensymphony.xwork2.ognl.ObjectProxy;
-import com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory;
-import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
 import com.opensymphony.xwork2.ognl.OgnlUtil;
 import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
 import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
 import com.opensymphony.xwork2.util.ValueStackFactory;
 import com.opensymphony.xwork2.util.WildcardHelper;
 import com.opensymphony.xwork2.util.location.LocatableProperties;
-import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
 import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
 import com.opensymphony.xwork2.validator.ActionValidatorManager;
 import com.opensymphony.xwork2.validator.AnnotationActionValidatorManager;
 import com.opensymphony.xwork2.validator.ValidatorFileParser;
 import ognl.MethodAccessor;
 import ognl.PropertyAccessor;
+import org.mvel2.integration.Listener;
+import org.mvel2.integration.PropertyHandler;
 
 import java.util.ArrayList;
 import java.util.Collection;
                .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
                .factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
                .factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
-               .factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
-               .factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
+               //.factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
+               //.factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
                .factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
                .factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
                .factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
                .factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
-               .factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
                
                // silly workarounds for ognl since there is no way to flush its caches
                .factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
                .factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
                .factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
                .factory(OgnlUtil.class, Scope.SINGLETON)
-               .factory(XWorkBasicConverter.class, Scope.SINGLETON);
+               .factory(XWorkBasicConverter.class, Scope.SINGLETON)
+
+               .factory(MVELUtil.class, Scope.SINGLETON)
+               .factory(com.opensymphony.xwork2.mvel.CompoundRootAccessor.class, Scope.SINGLETON)
+               .factory(ReflectionProvider.class, MVELReflectionProvider.class, Scope.SINGLETON)
+               .factory(Listener.class, MVELGetListener.class, Scope.SINGLETON)
+               .factory(PropertyHandler.class, List.class.getName(), MVELListPropertyAccessor.class, Scope.SINGLETON)
+               .factory(PropertyHandler.class, "nullHandler", MVELNullPropertyHandlerWrapper.class, Scope.SINGLETON)
+                ;
         props.setProperty("devMode", Boolean.FALSE.toString());
     }
 

src/java/com/opensymphony/xwork2/mvel/CompoundRootAccessor.java

+package com.opensymphony.xwork2.mvel;
+
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.mvel.accessors.DefaultVariableResolverFactory;
+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.mvel2.CompileException;
+import org.mvel2.MVEL;
+
+import java.io.Serializable;
+import java.util.Map;
+
+
+public class CompoundRootAccessor {
+
+    private final static Logger LOG = LoggerFactory.getLogger(CompoundRootAccessor.class);
+    static boolean devMode = false;
+    protected XWorkConverter converter;
+    protected MVELUtil mvelUtil;
+
+    @Inject("devMode")
+    public static void setDevMode(String mode) {
+        devMode = "true".equals(mode);
+    }
+
+    @Inject
+    public void setMvelUtil(MVELUtil mvelUtil) {
+        this.mvelUtil = mvelUtil;
+    }
+
+    @Inject
+    public void setConverter(XWorkConverter converter) {
+        this.converter = converter;
+    }
+
+    public void setProperty(MVELContext context, CompoundRoot root, String property, Object value) {
+        //set "top"
+        if (root.size() > 0)
+            context.put("top", root.get(0));
+
+        for (Object object : root) {
+            if (object == null) {
+                continue;
+            }
+
+            try {
+                if (object instanceof Map) {
+                    Map<Object, Object> map = (Map) object;
+                    map.put(property, value);
+                    return;
+                }
+
+                //TODO: add cache of expr
+                mvelUtil.setProperty(object, property, context, value);
+                return;
+            } catch (CompileException e) {
+                // this is OK if this happens, we'll just keep trying the next one
+                if (LOG.isTraceEnabled())
+                    LOG.trace("Failed to set property [#0] in class [#1]", e, property, object.getClass().getName());
+            } catch (Exception e) {
+                throw new XWorkException("unable to set '" + property + "' with value '" + value + "'", e);
+            }
+        }
+
+        Boolean reportError = (Boolean) context.get(ValueStack.REPORT_ERRORS_ON_NO_PROP);
+
+        final String msg = "No object in the CompoundRoot has a publicly accessible property named '" + property + "' (no setter could be found).";
+
+        if ((reportError != null) && (reportError.booleanValue())) {
+            throw new XWorkException(msg);
+        } else if (devMode) {
+            LOG.warn(msg);
+        }
+    }
+
+    public Object getProperty(MVELContext context, CompoundRoot target, String name) {
+        return getProperty(context, target, name, null);
+    }
+
+    public Object getProperty(MVELContext context, CompoundRoot root, String property, Class asType) {
+        //TODO: add cache of expr
+        Serializable expr = MVEL.compileExpression(property);
+
+        //set "top"
+        if (root.size() > 0)
+            context.put("top", root.get(0));
+
+        try {
+            Object result = mvelUtil.getProperty(root, expr, context);
+
+            return result;
+        } catch (Exception e) {
+            //ignore and move on
+        }
+
+        //try to get the property on the root itself, so [0].x.y
+        if ("top".equals(property)) {
+            if (root.size() > 0) {
+                return root.get(0);
+            } else {
+                return null;
+            }
+        }
+
+        for (Object object : root) {
+            try {
+                Object result =  mvelUtil.getProperty(object, expr, context);
+
+                if (result == null)
+                    return null;
+
+                if (asType != null)
+                    return converter.convertValue(context, result, asType);
+
+
+                return result;
+            } catch (CompileException e) {
+                //property was not found, ignore and move on
+            }
+        }
+
+        return null;
+    }
+
+    public Object callMethod(Map context, Object target, String name, Object[] objects) {
+        return null;
+    }
+
+    public Object callStaticMethod(Map transientVars, Class aClass, String s, Object[] objects) {
+        return null;
+    }
+}
+

src/java/com/opensymphony/xwork2/mvel/MVELContext.java

+package com.opensymphony.xwork2.mvel;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class MVELContext extends LinkedHashMap<String, Object> {
+    public MVELContext(Map<String, Object> context) {
+        super.putAll(context);
+    }
+
+    public MVELContext() {
+    }
+}

src/java/com/opensymphony/xwork2/mvel/MVELReflectionProvider.java

+package com.opensymphony.xwork2.mvel;
+
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+import com.opensymphony.xwork2.util.reflection.ReflectionException;
+import com.opensymphony.xwork2.inject.Inject;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.util.Map;
+import java.util.Collection;
+
+import org.mvel2.MVEL;
+import org.mvel2.util.PropertyTools;
+
+public class MVELReflectionProvider implements ReflectionProvider {
+    private MVELUtil mvelUtil;
+
+    @Inject
+    public void setMvelUtil(MVELUtil mvelUtil) {
+        this.mvelUtil = mvelUtil;
+    }
+
+    public Method getGetMethod(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException {
+        return mvelUtil.getGetMethod(targetClass, propertyName);
+    }
+
+    public Method getSetMethod(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException {
+        return mvelUtil.getSetMethod(targetClass, propertyName);
+    }
+
+    public Field getField(Class inClass, String name) {
+        Member member = PropertyTools.getFieldOrAccessor(inClass, name);
+        return (member instanceof Field) ? (Field) member : null;
+    }
+
+    public void setProperties(Map<String, String> props, Object o, Map<String, Object> context) {
+        throw new UnsupportedOperationException();
+    }
+
+    public void setProperties(Map<String, String> props, Object o, Map<String, Object> context, boolean throwPropertyExceptions) throws ReflectionException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void setProperties(Map<String, String> properties, Object o) {
+        throw new UnsupportedOperationException();
+    }
+
+    public PropertyDescriptor getPropertyDescriptor(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException {
+        return mvelUtil.getPropertyDescriptor(targetClass, propertyName);
+    }
+
+    public void copy(Object from, Object to, Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object getRealTarget(String property, Map<String, Object> context, Object root) throws ReflectionException {
+        return root;
+    }
+
+    public void setProperty(String name, Object value, Object o, Map<String, Object> context, boolean throwPropertyExceptions) {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void setProperty(String name, Object value, Object o, Map<String, Object> context) {
+        throw new UnsupportedOperationException();
+    }
+
+    public Map<String, Object> getBeanMap(Object source) throws IntrospectionException, ReflectionException {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object getValue(String expression, Map<String, Object> context, Object root) throws ReflectionException {
+        throw new UnsupportedOperationException();
+    }
+
+    public void setValue(String expression, Map<String, Object> context, Object root, Object value) throws ReflectionException {
+        mvelUtil.setProperty(root, expression, context, value);
+    }
+
+    public PropertyDescriptor[] getPropertyDescriptors(Object source) throws IntrospectionException {
+        return mvelUtil.loadPropertyDescriptors(source.getClass());
+    }
+}

src/java/com/opensymphony/xwork2/mvel/MVELTypeConverterWrapper.java

+/*
+ * $Id$
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.opensymphony.xwork2.mvel;
+
+import org.mvel2.ConversionHandler;
+import com.opensymphony.xwork2.conversion.TypeConverter;
+
+import java.util.Map;
+import java.lang.reflect.Member;
+
+public class MVELTypeConverterWrapper implements ConversionHandler {
+    private TypeConverter typeConverter;
+
+    public MVELTypeConverterWrapper(TypeConverter conv) {
+        if (conv == null) {
+            throw new IllegalArgumentException("Wrapped type converter cannot be null");
+        }
+        this.typeConverter = conv;
+    }
+
+    public Object convertValue(Map context, Object target, Member member,
+            String propertyName, Object value, Class toType) {
+        return typeConverter.convertValue(context, target, member, propertyName, value, toType);
+    }
+
+    public TypeConverter getTarget() {
+        return typeConverter;
+    }
+    public Object convertFrom(Object in) {
+        return typeConverter.convertValue(null, )
+    }
+
+    public boolean canConvertFrom(Class cls) {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+}

src/java/com/opensymphony/xwork2/mvel/MVELUtil.java

+package com.opensymphony.xwork2.mvel;
+
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.reflection.ReflectionException;
+import com.opensymphony.xwork2.mvel.accessors.DefaultVariableResolverFactory;
+
+import java.beans.*;
+import java.util.WeakHashMap;
+import java.util.Map;
+import java.util.List;
+import java.util.Collection;
+import java.util.HashMap;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.io.Serializable;
+
+import org.mvel2.integration.PropertyHandler;
+import org.mvel2.integration.PropertyHandlerFactory;
+import org.mvel2.MVEL;
+import org.mvel2.PropertyAccessor;
+import org.mvel2.util.PropertyTools;
+
+public class MVELUtil {
+    protected WeakHashMap<Class, HashMap<String, PropertyDescriptor>> descriptorsCache = new WeakHashMap<Class, HashMap<String, PropertyDescriptor>>();
+
+    public Method getSetMethod(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException {
+        //TODO : this stinks
+        if (Map.class.isAssignableFrom(targetClass))
+            return getMethod(targetClass, "put", Object.class, Object.class);
+        else if (List.class.isAssignableFrom(targetClass))
+            return getMethod(targetClass, "add", int.class, Object.class);
+        else if (Collection.class.isAssignableFrom(targetClass))
+            return getMethod(targetClass, "add", Object.class);
+
+        return PropertyTools.getSetter(targetClass, propertyName);
+    }
+
+    public Method getGetMethod(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException {
+        //TODO : this stinks
+        if (Map.class.isAssignableFrom(targetClass))
+            return getMethod(targetClass, "get", Object.class);
+        else if (List.class.isAssignableFrom(targetClass))
+            return getMethod(targetClass, "get", int.class);
+
+        return PropertyTools.getGetter(targetClass, propertyName);
+    }
+
+    private Method getMethod(Class clazz, String name, Class... args) {
+        try {
+            return clazz.getMethod(name, args);
+        } catch (NoSuchMethodException e) {
+            //cannot happen
+        }
+        return null;
+    }
+
+    public void setProperty(Object target, String expr, Map<String, Object> context, Object value) {
+        MVELContext realContex = (context instanceof MVELContext) ? (MVELContext) context : new MVELContext(context);
+        MVEL.executeSetExpression(MVEL.compileSetExpression(expr), target, new DefaultVariableResolverFactory(realContex), value);
+    }
+
+    public Object getProperty(Object target, Serializable expr, Map<String, Object> context) {
+        MVELContext realContex = (context instanceof MVELContext) ? (MVELContext) context : new MVELContext(context);
+        return MVEL.executeExpression(expr, target, new DefaultVariableResolverFactory(realContex));
+    }
+
+    public PropertyDescriptor[] loadPropertyDescriptors(Class beanClass) {
+        // Look up any cached descriptors for this bean class
+        HashMap<String, PropertyDescriptor> descriptors = descriptorsCache.get(beanClass);
+        if (descriptors != null)
+            return (PropertyDescriptor[]) descriptors.values().toArray(new PropertyDescriptor[0]);
+
+        // Introspect the bean and cache the generated descriptors
+        BeanInfo beanInfo = null;
+        try {
+            beanInfo = Introspector.getBeanInfo(beanClass);
+        } catch (IntrospectionException e) {
+            return (new PropertyDescriptor[0]);
+        }
+        PropertyDescriptor[] descriptorsArray = beanInfo.getPropertyDescriptors();
+        if (descriptorsArray != null) {
+            descriptors = new HashMap<String, PropertyDescriptor>();
+            for (PropertyDescriptor propertyDescriptor : descriptorsArray)
+                descriptors.put(propertyDescriptor.getName(), propertyDescriptor);
+            descriptorsCache.put(beanClass, descriptors);
+        }
+
+        return descriptorsArray;
+    }
+
+    public PropertyDescriptor getPropertyDescriptor(Class beanClass, String name) {
+        //make sure properties descriptors are loaded
+        loadPropertyDescriptors(beanClass);
+        HashMap<String, PropertyDescriptor> descriptors = descriptorsCache.get(beanClass);
+        return descriptors != null ? descriptors.get(name) : null;
+    }
+}

src/java/com/opensymphony/xwork2/mvel/MVELValueStack.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.mvel;
+
+import com.opensymphony.xwork2.*;
+import com.opensymphony.xwork2.SecurityMemberAccess;
+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.MemberAccessValueStack;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.util.logging.LoggerUtils;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+
+
+import java.beans.IntrospectionException;
+import java.beans.PropertyDescriptor;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+
+import org.mvel2.MVEL;
+import org.mvel2.util.ReflectionUtil;
+
+/**
+ * Ognl implementation of a value stack that allows for dynamic Ognl expressions to be evaluated against it. When
+ * evaluating an expression, the stack will be searched down the stack, from the latest objects pushed in to the
+ * earliest, looking for a bean with a getter or setter for the given property or a method of the given name (depending
+ * on the expression being evaluated).
+ */
+public class MVELValueStack implements Serializable, ValueStack, ClearableValueStack, MemberAccessValueStack {
+
+    private static Logger LOG = LoggerFactory.getLogger(MVELValueStack.class);
+    protected boolean devMode;
+
+    
+    protected CompoundRoot root;
+    protected transient MVELContext context;
+    protected Class defaultType;
+    protected Map<Object, Object> overrides;
+
+    protected transient MVELUtil mvelUtil;
+    protected transient SecurityMemberAccess securityMemberAccess;
+    protected transient CompoundRootAccessor compoundRootAccessor;
+
+    protected MVELValueStack(XWorkConverter xworkConverter, CompoundRootAccessor accessor, TextProvider prov, boolean allowStaticAccess) {
+        setRoot(xworkConverter, accessor, new CompoundRoot(), allowStaticAccess);
+        push(prov);
+    }
+
+
+    protected MVELValueStack(ValueStack vs, XWorkConverter xworkConverter, CompoundRootAccessor accessor, boolean allowStaticAccess) {
+        setRoot(xworkConverter, accessor, new CompoundRoot(vs.getRoot()), allowStaticAccess);
+    }
+
+    protected void setRoot(XWorkConverter xworkConverter,
+                           CompoundRootAccessor accessor, CompoundRoot compoundRoot, boolean allowStaticMethodAccess) {
+        this.root = compoundRoot;
+        this.securityMemberAccess = new SecurityMemberAccess(allowStaticMethodAccess);
+        this.context = new MVELContext();
+        this.compoundRootAccessor = accessor;
+        context.put(VALUE_STACK, this);
+    }
+
+    @Inject("devMode")
+    public void setDevMode(String mode) {
+        devMode = "true".equalsIgnoreCase(mode);
+    }
+
+    @Inject
+    public void setMVELUtil(MVELUtil mvelUtil) {
+        this.mvelUtil = mvelUtil;
+    }
+
+    public Map<String, Object> getContext() {
+        return context;
+    }
+
+
+    public void setDefaultType(Class defaultType) {
+        this.defaultType = defaultType;
+    }
+
+
+    public void setExprOverrides(Map<Object, Object> overrides) {
+        if (this.overrides == null) {
+            this.overrides = overrides;
+        } else {
+            this.overrides.putAll(overrides);
+        }
+    }
+
+    public Map<Object, Object> getExprOverrides() {
+        return this.overrides;
+    }
+
+
+    public CompoundRoot getRoot() {
+        return root;
+    }
+
+
+    public void setValue(String expr, Object value) {
+        setValue(expr, value, devMode);
+    }
+
+
+    public void setValue(String expr, Object value, boolean throwExceptionOnFailure) {
+        try {
+            context.put(XWorkConverter.CONVERSION_PROPERTY_FULLNAME, expr);
+            context.put(REPORT_ERRORS_ON_NO_PROP, (throwExceptionOnFailure) ? Boolean.TRUE : Boolean.FALSE);
+            compoundRootAccessor.setProperty(context, root, expr, value);
+        } catch (Exception re) { //XW-281
+            if (throwExceptionOnFailure) {
+                StringBuilder msg = new StringBuilder();
+                msg.append("Error setting expression '");
+                msg.append(expr);
+                msg.append("' with value ");
+
+                if (value instanceof Object[]) {
+                    Object[] valueArray = (Object[]) value;
+                    msg.append("[");
+                    for (int index = 0; index < valueArray.length; index++) {
+                        msg.append("'");
+                        msg.append(valueArray[index]);
+                        msg.append("'");
+
+                        if (index < (valueArray.length + 1))
+                            msg.append(", ");
+                    }
+                    msg.append("]");
+                } else {
+                    msg.append("'");
+                    msg.append(value);
+                    msg.append("'");
+                }
+
+                throw new XWorkException(msg.toString(), re);
+            } else {
+                if (LOG.isWarnEnabled()) {
+                    LOG.warn("Error setting value", re);
+                }
+            }
+        } finally {
+            ReflectionContextState.clear(context);
+            context.remove(XWorkConverter.CONVERSION_PROPERTY_FULLNAME);
+            context.remove(REPORT_ERRORS_ON_NO_PROP);
+        }
+    }
+
+
+    public String findString(String expr) {
+        return (String) findValue(expr, String.class);
+    }
+
+
+    public Object findValue(String expr) {
+        try {
+            if (expr == null) {
+                return null;
+            }
+
+            if ((overrides != null) && overrides.containsKey(expr)) {
+                expr = (String) overrides.get(expr);
+            }
+
+            if (defaultType != null) {
+                return findValue(expr, defaultType);
+            }
+
+            Object value = compoundRootAccessor.getProperty(context, root, expr);
+            if (value != null) {
+                return value;
+            } else {
+                checkForInvalidProperties(expr);
+                return findInContext(expr);
+            }
+        } catch (Exception e) {
+            logLookupFailure(expr, e);
+
+            return findInContext(expr);
+        } finally {
+            ReflectionContextState.clear(context);
+        }
+    }
+
+
+    public Object findValue(String expr, Class asType) {
+        try {
+            if (expr == null) {
+                return null;
+            }
+
+            if ((overrides != null) && overrides.containsKey(expr)) {
+                expr = (String) overrides.get(expr);
+            }
+
+            Object value =  compoundRootAccessor.getProperty(context, root, expr, asType);
+            if (value != null) {
+                return value;
+            } else {
+                return findInContext(expr);
+            }
+        } catch (Exception e) {
+            logLookupFailure(expr, e);
+
+            return findInContext(expr);
+        } finally {
+            ReflectionContextState.clear(context);
+        }
+    }
+
+    private Object findInContext(String name) {
+        return getContext().get(name);
+    }
+
+
+    private void checkForInvalidProperties(String expr) {
+        if (expr.contains("(") && expr.contains(")")) {
+            if (devMode) {
+                LOG.warn("Could not find method [" + expr + "]");
+            }
+        } else if (findInContext(expr) == null) {
+            // find objects with Action in them and inspect matching getters
+            Set<String> availableProperties = new LinkedHashSet<String>();
+            for (Object o : root) {
+                if (o instanceof ActionSupport || o.getClass().getSimpleName().endsWith("Action")) {
+                    try {
+                        findAvailableProperties(o.getClass(), expr, availableProperties, null);
+                    } catch (IntrospectionException ise) {
+                        // ignore
+                    }
+                }
+            }
+            if (!availableProperties.contains(expr)) {
+                if (devMode) {
+                    LOG.warn("Could not find property [" + expr + "]");
+                }
+            }
+        }
+    }
+
+
+    private void findAvailableProperties(Class c, String expr, Set<String> availableProperties, String parent) throws IntrospectionException {
+        PropertyDescriptor[] descriptors = mvelUtil.loadPropertyDescriptors(c);
+        for (PropertyDescriptor pd : descriptors) {
+            String name = pd.getDisplayName();
+            if (parent != null && expr.contains(".")) {
+                name = expr.substring(0, expr.indexOf(".") + 1) + name;
+            }
+            if (expr.startsWith(name)) {
+                availableProperties.add((parent != null) ? parent + "." + name : name);
+                if (expr.equals(name)) break; // no need to go any further
+                if (expr.contains(".")) {
+                    String property = expr.substring(expr.indexOf(".") + 1);
+                    // if there is a nested property (indicated by a dot), chop it off so we can look for method name
+                    String rawProperty = (property.contains(".")) ? property.substring(0, property.indexOf(".")) : property;
+                    String methodToLookFor = ReflectionUtil.getGetter(rawProperty);
+                    Method[] methods = pd.getPropertyType().getDeclaredMethods();
+                    for (Method method : methods) {
+                        if (method.getName().equals(methodToLookFor)) {
+                            availableProperties.add(name + "." + rawProperty);
+                            Class returnType = method.getReturnType();
+                            findAvailableProperties(returnType, property, availableProperties, name);
+                        }
+                    }
+
+                }
+            }
+        }
+    }
+
+
+    private void logLookupFailure(String expr, Exception e) {
+        String msg = LoggerUtils.format("Caught an exception while evaluating expression '#0' against value stack", expr);
+        if (devMode && LOG.isWarnEnabled()) {
+            LOG.warn(msg, e);
+            LOG.warn("NOTE: Previous warning message was issued due to devMode set to true.");
+        } else if (LOG.isDebugEnabled()) {
+            LOG.debug(msg, e);
+        }
+    }
+
+
+    public Object peek() {
+        return root.peek();
+    }
+
+
+    public Object pop() {
+        return root.pop();
+    }
+
+
+    public void push(Object o) {
+        root.push(o);
+    }
+
+
+    public void set(String key, Object o) {
+        //set basically is backed by a Map
+        //pushed on the stack with a key
+        //being put on the map and the
+        //Object being the value
+
+        Map setMap = null;
+
+        //check if this is a Map
+        //put on the stack  for setting
+        //if so just use the old map (reduces waste)
+        Object topObj = peek();
+        if (topObj instanceof Map
+                && ((Map) topObj).get(MAP_IDENTIFIER_KEY) != null) {
+
+            setMap = (Map) topObj;
+        } else {
+            setMap = new HashMap();
+            //the map identifier key ensures
+            //that this map was put there
+            //for set purposes and not by a user
+            //whose data we don't want to touch
+            setMap.put(MAP_IDENTIFIER_KEY, "");
+            push(setMap);
+        }
+        setMap.put(key, o);
+
+    }
+
+
+    private static final String MAP_IDENTIFIER_KEY = "com.opensymphony.xwork2.util.OgnlValueStack.MAP_IDENTIFIER_KEY";
+
+
+    public int size() {
+        return root.size();
+    }
+
+   /* private Object readResolve() {
+        // TODO: this should be done better
+        ActionContext ac = ActionContext.getContext();
+        Container cont = ac.getContainer();
+        XWorkConverter xworkConverter = cont.getInstance(XWorkConverter.class);
+        CompoundRootAccessor accessor = (CompoundRootAccessor) cont.getInstance(PropertyAccessor.class, CompoundRoot.class.getName());
+        TextProvider prov = cont.getInstance(TextProvider.class, "system");
+        boolean allow = "true".equals(cont.getInstance(String.class, "allowStaticMethodAccess"));
+        MVELlValueStack aStack = new MVELlValueStack(xworkConverter, accessor, prov, allow);
+        aStack.setOgnlUtil(cont.getInstance(OgnlUtil.class));
+        aStack.setRoot(xworkConverter, accessor, this.root, allow);
+
+        return aStack;
+    }
+*/
+
+    public void clearContextValues() {
+        context.clear();
+    }
+
+    public void setAcceptProperties(Set<Pattern> acceptedProperties) {
+        securityMemberAccess.setAcceptProperties(acceptedProperties);
+    }
+
+    public void setExcludeProperties(Set<Pattern> excludeProperties) {
+        securityMemberAccess.setExcludeProperties(excludeProperties);
+    }
+}

src/java/com/opensymphony/xwork2/mvel/MVELValueStackFactory.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.mvel;
+
+import com.opensymphony.xwork2.ActionContext;
+import com.opensymphony.xwork2.TextProvider;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.conversion.NullHandler;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ValueStackFactory;
+
+import java.util.Map;
+import java.util.Set;
+
+import org.mvel2.integration.*;
+import org.mvel2.MVEL;
+
+/**
+ * Creates an MVEL value stack
+ */
+public class MVELValueStackFactory implements ValueStackFactory {
+
+    private XWorkConverter xworkConverter;
+    private CompoundRootAccessor compoundRootAccessor;
+    private TextProvider textProvider;
+    private Container container;
+    private boolean allowStaticMethodAccess;
+
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+
+    @Inject("system")
+    public void setTextProvider(TextProvider textProvider) {
+        this.textProvider = textProvider;
+    }
+
+    @Inject(value = "allowStaticMethodAccess", required = false)
+    public void setAllowStaticMethodAccess(String allowStaticMethodAccess) {
+        this.allowStaticMethodAccess = "true".equalsIgnoreCase(allowStaticMethodAccess);
+    }
+
+    public ValueStack createValueStack() {
+        ValueStack stack = new MVELValueStack(xworkConverter, compoundRootAccessor, textProvider, allowStaticMethodAccess);
+        container.inject(stack);
+        stack.getContext().put(ActionContext.CONTAINER, container);
+        return stack;
+    }
+
+    public ValueStack createValueStack(ValueStack stack) {
+        ValueStack result = new MVELValueStack(stack, xworkConverter, compoundRootAccessor, allowStaticMethodAccess);
+        container.inject(result);
+        stack.getContext().put(ActionContext.CONTAINER, container);
+        return result;
+    }
+
+    @Inject
+    public void setContainer(Container container) throws ClassNotFoundException {
+        Set<String> names = container.getInstanceNames(PropertyHandler.class);
+        if (names != null) {
+            MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING = true;
+            for (String name : names) {
+                //the handler for nullHandler does not handle an specific class
+                //but null values on any class
+                if (!"nullHandler".equals(name)) {
+                    Class cls = Class.forName(name);
+                    if (cls != null) {
+                        PropertyHandler instance = container.getInstance(PropertyHandler.class, name);
+                        PropertyHandlerFactory.registerPropertyHandler(cls, instance);
+                    }
+                }
+            }
+        }
+
+        compoundRootAccessor = (CompoundRootAccessor) container.getInstance(CompoundRootAccessor.class);
+
+        PropertyHandler nullHandler = container.getInstance(PropertyHandler.class, "nullHandler");
+        //what if it is null? oh the irony
+        if (nullHandler != null) {
+            PropertyHandlerFactory.setNullMethodHandler(nullHandler);
+            PropertyHandlerFactory.setNullPropertyHandler(nullHandler);
+        }
+
+        Listener objectListener = container.getInstance(Listener.class);
+        if (objectListener != null) {
+            GlobalListenerFactory.registerGetListener(objectListener);
+            GlobalListenerFactory.registerSetListener(objectListener);
+        }
+
+        /*names = container.getInstanceNames(MethodAccessor.class);
+        if (names != null) {
+            for (String name : names) {
+                Class cls = Class.forName(name);
+                if (cls != null) {
+                    OgnlRuntime.setMethodAccessor(cls, container.getInstance(MethodAccessor.class, name));
+                }
+            }
+        }
+
+        }*/
+
+        if (compoundRootAccessor == null) {
+            throw new IllegalStateException("Couldn't find the compound root accessor");
+        }
+        this.container = container;
+    }
+}

src/java/com/opensymphony/xwork2/mvel/accessors/DefaultVariableResolverFactory.java

+package com.opensymphony.xwork2.mvel.accessors;
+
+import org.mvel2.integration.impl.BaseVariableResolverFactory;
+import org.mvel2.integration.impl.MapVariableResolverFactory;
+import org.mvel2.integration.VariableResolver;
+import com.opensymphony.xwork2.mvel.MVELContext;
+import com.opensymphony.xwork2.util.CompoundRoot;
+
+public class DefaultVariableResolverFactory extends BaseVariableResolverFactory {
+    private MVELContext context;
+
+    public DefaultVariableResolverFactory(MVELContext context) {
+        this.appendFactory(new MapVariableResolverFactory(context));
+
+        this.context = context;
+    }
+
+    public VariableResolver createVariable(String name, Object value) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public VariableResolver createVariable(String name, Object value, Class<?> type) {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean isTarget(String name) {
+        return false;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public boolean isResolveable(String name) {
+        return nextFactory.isResolveable(name);
+    }
+
+    public MVELContext getContext() {
+        return context;
+    }
+}

src/java/com/opensymphony/xwork2/mvel/accessors/MVELGetListener.java

+package com.opensymphony.xwork2.mvel.accessors;
+
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.mvel.MVELContext;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import org.mvel2.integration.Listener;
+import org.mvel2.integration.VariableResolverFactory;
+
+public class MVELGetListener implements Listener {
+    public void onEvent(Object target, String property, VariableResolverFactory variableFactory, Object value) {
+        DefaultVariableResolverFactory factory = (DefaultVariableResolverFactory) variableFactory;
+        MVELContext context = factory.getContext();
+        context.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, target.getClass());
+        context.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, property);
+        ReflectionContextState.updateCurrentPropertyPath(context, property);
+    }
+}

src/java/com/opensymphony/xwork2/mvel/accessors/MVELListPropertyAccessor.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.mvel.accessors;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.mvel.MVELContext;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.ognl.accessor.XWorkCollectionPropertyAccessor;
+import com.opensymphony.xwork2.ognl.OgnlUtil;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import com.opensymphony.xwork2.util.TextUtils;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.mvel2.integration.PropertyHandler;
+import org.mvel2.integration.VariableResolverFactory;
+import org.mvel2.optimizers.impl.asm.ProducesBytecode;
+import org.mvel2.asm.MethodVisitor;
+
+public class MVELListPropertyAccessor implements PropertyHandler, ProducesBytecode {
+
+    private XWorkCollectionPropertyAccessor _sAcc = new XWorkCollectionPropertyAccessor();
+
+    private XWorkConverter xworkConverter;
+    private ObjectFactory objectFactory;
+    private ObjectTypeDeterminer objectTypeDeterminer;
+    private OgnlUtil ognlUtil;
+
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+
+    @Inject
+    public void setObjectFactory(ObjectFactory fac) {
+        this.objectFactory = fac;
+    }
+
+    @Inject
+    public void setObjectTypeDeterminer(ObjectTypeDeterminer ot) {
+        this.objectTypeDeterminer = ot;
+    }
+
+    public Object getProperty(String name, Object contextObj, VariableResolverFactory variableFactory) {
+        DefaultVariableResolverFactory defaultFactory = (DefaultVariableResolverFactory) variableFactory;
+        MVELContext context = defaultFactory.getContext();
+        List list = (List) contextObj;
+
+        Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+        String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+
+        Long numericValue = null;
+        try {
+            numericValue = Long.valueOf(name);
+        } catch (NumberFormatException e) {
+            //ignore
+        }
+        
+        if (numericValue != null
+                && ReflectionContextState.isCreatingNullObjects(context)
+                && objectTypeDeterminer.shouldCreateIfNew(lastClass, lastProperty, contextObj, null, true)) {
+            int index = numericValue.intValue();
+            int listSize = list.size();
+
+            /*if (lastClass == null || lastProperty == null) {
+                return super.getProperty(context, target, name);
+            }*/
+            Class beanClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, name);
+            if (listSize <= index) {
+                Object result = null;
+
+                for (int i = listSize; i < index; i++) {
+                    list.add(null);
+                }
+                try {
+                    list.add(index, result = objectFactory.buildBean(beanClass, context));
+                } catch (Exception exc) {
+                    throw new XWorkException(exc);
+                }
+                return result;
+            } else if (list.get(index) == null) {
+                Object result = null;
+                try {
+                    list.set(index, result = objectFactory.buildBean(beanClass, context));
+                } catch (Exception exc) {
+                    throw new XWorkException(exc);
+                }
+                return result;
+            } else {
+                return list.get(index);
+            }
+        }
+
+        return null;
+    }
+
+    public Object setProperty(String name, Object target, VariableResolverFactory variableFactory, Object value) {
+        DefaultVariableResolverFactory defaultFactory = (DefaultVariableResolverFactory) variableFactory;
+        MVELContext context = defaultFactory.getContext();
+
+        Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+        String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+        Class convertToClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, name);
+
+        if (name instanceof String && value.getClass().isArray()) {
+            // looks like the input game in the form of "someList.foo" and
+            // we are expected to define the index values ourselves.
+            // So let's do it:
+
+            Collection c = (Collection) value;
+            Object[] values = (Object[]) value;
+            for (Object v : values) {
+                try {
+                    Object o = objectFactory.buildBean(convertToClass, context);
+                    ognlUtil.setValue((String) name, context, o, v);
+                    c.add(o);
+                } catch (Exception e) {
+                    throw new XWorkException("Error converting given String values for Collection.", e);
+                }
+            }
+
+            // we don't want to do the normal list property setting now, since we've already done the work
+            // just return instead
+            return null;
+        }
+
+        Object realValue = getRealValue(context, value, convertToClass);
+
+        Long numericValue = null;
+        try {
+            numericValue = Long.valueOf(name);
+        } catch (NumberFormatException e) {
+            //ignore
+        }
+
+        if (target instanceof List && numericValue != null) {
+            //make sure there are enough spaces in the List to set
+            List list = (List) target;
+            int listSize = list.size();
+            int count = numericValue.intValue();
+            if (count >= listSize) {
+                for (int i = listSize; i <= count; i++) {
+                    list.add(null);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    private Object getRealValue(Map context, Object value, Class convertToClass) {
+        if (value == null || convertToClass == null) {
+            return value;
+        }
+        return xworkConverter.convertValue(context, value, convertToClass);
+    }
+
+    public void produceBytecodeGet(MethodVisitor mv, String propertyName, VariableResolverFactory factory) {
+    }
+
+    public void produceBytecodePut(MethodVisitor mv, String propertyName, VariableResolverFactory factory) {
+    }
+}

src/java/com/opensymphony/xwork2/mvel/accessors/MVELNullPropertyHandlerWrapper.java

+package com.opensymphony.xwork2.mvel.accessors;
+
+import com.opensymphony.xwork2.conversion.NullHandler;
+import com.opensymphony.xwork2.mvel.accessors.DefaultVariableResolverFactory;
+import com.opensymphony.xwork2.mvel.MVELContext;
+import com.opensymphony.xwork2.inject.Inject;
+
+import java.util.Map;
+import java.util.Collections;
+
+import org.mvel2.integration.VariableResolverFactory;
+import org.mvel2.integration.PropertyHandler;
+
+public class MVELNullPropertyHandlerWrapper implements PropertyHandler{
+    private NullHandler wrapped;
+
+    @Inject("java.lang.Object")
+    public void setWrapped(NullHandler wrapped) {
+        this.wrapped = wrapped;
+    }
+
+    public Object getProperty(String property, Object target, VariableResolverFactory variableFactory) {
+        String propertyName = property.startsWith("get") ? property.substring(3, 5).toLowerCase() + property.substring(5) : property;
+        MVELContext contex = ((DefaultVariableResolverFactory)variableFactory).getContext();
+        return wrapped.nullPropertyValue(contex, target, propertyName);
+    }
+
+    public Object setProperty(String method, Object target, VariableResolverFactory variableFactory, Object value) {
+        String methodName = method.startsWith("set") ? method.substring(3, 5).toLowerCase() + method.substring(5) : method;
+        MVELContext contex = ((DefaultVariableResolverFactory)variableFactory).getContext();
+        return wrapped.nullMethodResult(contex, target, methodName, new Object[] {value});
+    }
+}

src/java/com/opensymphony/xwork2/ognl/OgnlValueStack.java

  */
 package com.opensymphony.xwork2.ognl;
 
-import com.opensymphony.xwork2.ActionContext;
-import com.opensymphony.xwork2.ActionSupport;
-import com.opensymphony.xwork2.TextProvider;
-import com.opensymphony.xwork2.XWorkException;
+import com.opensymphony.xwork2.*;
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
 import com.opensymphony.xwork2.inject.Container;
 import com.opensymphony.xwork2.inject.Inject;

src/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java

-/*
- * Copyright (c) 2002-2006 by OpenSymphony
- * All rights reserved.
- */
-package com.opensymphony.xwork2.ognl;
-
-import ognl.DefaultMemberAccess;
-
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Allows access decisions to be made on the basis of whether a member is static or not.
- * Also blocks or allows access to properties.
- */
-public class SecurityMemberAccess extends DefaultMemberAccess {
-
-    private boolean allowStaticMethodAccess;
-    Set<Pattern> excludeProperties = Collections.emptySet();
-    Set<Pattern> acceptProperties = Collections.emptySet();
-
-    public SecurityMemberAccess(boolean method) {
-        super(false);
-        allowStaticMethodAccess = method;
-    }
-
-    public boolean getAllowStaticMethodAccess() {
-        return allowStaticMethodAccess;
-    }
-
-    public void setAllowStaticMethodAccess(boolean allowStaticMethodAccess) {
-        this.allowStaticMethodAccess = allowStaticMethodAccess;
-    }
-
-    @Override
-    public boolean isAccessible(Map context, Object target, Member member,
-                                String propertyName) {
-
-        boolean allow = true;
-        int modifiers = member.getModifiers();
-        if (Modifier.isStatic(modifiers)) {
-            if (member instanceof Method && !getAllowStaticMethodAccess()) {
-                allow = false;
-                if (target instanceof Class) {
-                    Class clazz = (Class) target;
-                    Method method = (Method) member;
-                    if (Enum.class.isAssignableFrom(clazz) && method.getName().equals("values"))
-                        allow = true;
-                }
-            }
-        }
-
-        //failed static test
-        if (!allow)
-            return false;
-
-        // Now check for standard scope rules
-        if (!super.isAccessible(context, target, member, propertyName))
-            return false;
-
-        return isAcceptableProperty(propertyName);
-    }
-
-    protected boolean isAcceptableProperty(String name) {
-        if (isAccepted(name) && !isExcluded(name)) {
-            return true;
-        }
-        return false;
-    }
-
-    protected boolean isAccepted(String paramName) {
-        if (!this.acceptProperties.isEmpty()) {
-            for (Pattern pattern : acceptProperties) {
-                Matcher matcher = pattern.matcher(paramName);
-                if (matcher.matches()) {
-                    return true;
-                }
-            }
-
-            //no match, but acceptedParams is not empty
-            return false;
-        }
-
-        //empty acceptedParams
-        return true;
-    }
-
-    protected boolean isExcluded(String paramName) {
-        if (!this.excludeProperties.isEmpty()) {
-            for (Pattern pattern : excludeProperties) {
-                Matcher matcher = pattern.matcher(paramName);
-                if (matcher.matches()) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    public void setExcludeProperties(Set<Pattern> excludeProperties) {
-        this.excludeProperties = excludeProperties;
-    }
-
-    public void setAcceptProperties(Set<Pattern> acceptedProperties) {
-        this.acceptProperties = acceptedProperties;
-    }
-}

src/test/com/opensymphony/xwork2/SimpleAction.java

     private String aliasDest;
     private Map<String,String> protectedMap = new HashMap<String,String>();
     private Map<String,String> existingMap = new HashMap<String,String>();
+    private Map<String,Object> dummyMap = new HashMap<String,Object>();
     
     public static boolean resultCalled;
 
         resultCalled = false;
         existingMap.put("existingKey", "value");
     }
-    
+
+    public Map<String, Object> getDummyMap() {
+        return dummyMap;
+    }
+
     public Map<String,String> getTheProtectedMap() {
         return protectedMap;
     }

src/test/com/opensymphony/xwork2/mvel/CompoundRootAccessorTest.java

+package com.opensymphony.xwork2.mvel;
+
+import com.opensymphony.xwork2.mvel.CompoundRootAccessor;
+import com.opensymphony.xwork2.mvel.accessors.DefaultVariableResolverFactory;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ArrayUtils;
+import com.opensymphony.xwork2.Foo;
+import com.opensymphony.xwork2.SimpleAction;
+import com.opensymphony.xwork2.XWorkException;
+
+import junit.framework.TestCase;
+import org.mvel2.PropertyAccessor;
+import org.mvel2.MVEL;
+import org.mvel2.util.PropertyTools;
+import org.mvel2.integration.PropertyHandlerFactory;
+import org.mvel2.integration.PropertyHandler;
+import org.mvel2.integration.VariableResolverFactory;
+import org.mvel2.integration.Listener;
+import org.mvel2.integration.GlobalListenerFactory;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+public class CompoundRootAccessorTest extends TestCase {
+    private CompoundRootAccessor accessor;
+
+    public void testGetShortCircuitStack() {
+        CompoundRoot root = new CompoundRoot();
+
+        root.add(new Foo("value1"));
+        root.add(new Foo("value2"));
+
+        assertEquals("value1", accessor.getProperty(new MVELContext(), root, "name"));
+    }
+
+
+    public void testmvel() {
+        class Dummy {
+            List myList = Arrays.asList("test1", "test2");
+
+            public List getMyList() {
+                return myList;
+            }
+
+            public void setMyList(List myList) {
+                this.myList = myList;
+            }
+        }
+
+        PropertyHandlerFactory.registerPropertyHandler(List.class, new PropertyHandler() {
+            public Object getProperty(String name, Object contextObj, VariableResolverFactory variableFactory) {
+                throw new RuntimeException();
+            }
+
+            public Object setProperty(String name, Object contextObj, VariableResolverFactory variableFactory, Object value) {
+                throw new RuntimeException();
+            }
+        });
+
+        MVEL.setProperty(new Dummy(), "myList[0]", "test1");
+    }
+
+    public void testSetShortCircuitStack() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        SimpleAction action2 = new SimpleAction();
+
+        root.add(action);
+        root.add(action2);
+
+        accessor.setProperty(new MVELContext(), root, "bar", "10");
+        assertEquals(10, action.getBar());
+        assertEquals(0, action2.getBar());
+    }
+
+    public void testGetSimpleBean() {
+        CompoundRoot root = new CompoundRoot();
+
+        root.add(new Foo("value1"));
+
+        assertEquals("value1", accessor.getProperty(new MVELContext(), root, "name"));
+    }
+
+    public void testSetSimpleBean() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+
+        root.add(action);
+
+        accessor.setProperty(new MVELContext(), root, "bar", "10");
+        assertEquals(10, action.getBar());
+    }
+
+    public void testSetSimpleBeanReportError() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+
+        root.add(action);
+
+        MVELContext context = new MVELContext();
+        context.put(ValueStack.REPORT_ERRORS_ON_NO_PROP, true);
+
+        try {
+            accessor.setProperty(context, root, "bar_notthere", "10");
+            fail("Error was not reported while setting a property which was not found");
+        } catch (XWorkException e) {
+            //expected
+        }
+    }
+
+    public void testSetSimpleBeanDontReportError() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        root.add(action);
+
+        MVELContext context = new MVELContext();
+        context.put(ValueStack.REPORT_ERRORS_ON_NO_PROP, false);
+
+        accessor.setProperty(context, root, "bar_notthere", "10");
+    }
+
+    public void testGetSimpleBeanStacked() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        action.setBar(10);
+
+        root.add(new Foo("value1"));
+        root.add(action);
+
+        assertEquals(10, accessor.getProperty(new MVELContext(), root, "bar"));
+    }
+
+    public void testSetSimpleBeanStacked() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+
+        root.add(new Foo("value1"));
+        root.add(action);
+
+        accessor.setProperty(new MVELContext(), root, "bar", "10");
+        assertEquals(10, action.getBar());
+    }
+
+    public void testGetPropertyNotFound() {
+        CompoundRoot root = new CompoundRoot();
+
+        root.add(new Foo("value1"));
+
+        assertNull(accessor.getProperty(new MVELContext(), root, "name_not_there"));
+    }
+
+    public void testGetSimpleMap() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        action.getDummyMap().put("name", "value1");
+
+        root.add(action);
+
+        assertEquals("value1", accessor.getProperty(new MVELContext(), root, "dummyMap.name"));
+        assertEquals("value1", accessor.getProperty(new MVELContext(), root, "dummyMap['name']"));
+    }
+
+    public void testSetSimpleMap() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+
+        root.add(action);
+
+        accessor.setProperty(new MVELContext(), root, "dummyMap['name2']", "value2");
+
+        assertEquals("value2", action.getDummyMap().get("name2"));
+    }
+
+    public void testGetMapWithNestedBean() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        action.getDummyMap().put("foo", new Foo("value1"));
+        root.add(action);
+
+        //assertEquals("value1", accessor.getProperty(null, root, "dummyMap.foo.name"));
+        assertEquals("value1", accessor.getProperty(new MVELContext(), root, "dummyMap['foo'].name"));
+    }
+
+    public void testSetMapWithNestedBean() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        SimpleAction action2 = new SimpleAction();
+        action.getDummyMap().put("action", action2);
+
+        root.add(action);
+
+        accessor.setProperty(new MVELContext(), root, "dummyMap.action.bar", "10");
+        assertEquals(10, action2.getBar());
+
+        accessor.setProperty(new MVELContext(), root, "dummyMap['action'].bar", "11");
+        assertEquals(11, action2.getBar());
+    }
+
+
+    public void testGetSimpleMapWithContext() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        action.getDummyMap().put("name", "value1");
+        root.add(action);
+
+        MVELContext context = new MVELContext();
+        context.put("propertyName", "name");
+
+        assertEquals("value1", accessor.getProperty(context, root, "dummyMap[propertyName]"));
+    }
+
+    public void testSetSimpleMapWithContext() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        root.add(action);
+
+        MVELContext context = new MVELContext();
+        context.put("propertyName", "name");
+
+        accessor.setProperty(context, root, "dummyMap[propertyName]", "value1");
+
+        assertEquals("value1", action.getDummyMap().get("name"));
+    }
+
+    public void testGetSimpleMapWithContextAndNestedBean() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        action.getDummyMap().put("foo", new Foo("value1"));
+        root.add(action);
+
+        MVELContext context = new MVELContext();
+        context.put("propertyName", "foo");
+
+        assertEquals("value1", accessor.getProperty(context, root, "dummyMap[propertyName].name"));
+    }
+
+    public void testSetSimpleMapWithContextAndNestedBean() {
+        CompoundRoot root = new CompoundRoot();
+        SimpleAction action = new SimpleAction();
+        action.getDummyMap().put("foo", new Foo("value1"));
+        root.add(action);
+
+        MVELContext context = new MVELContext();
+        context.put("propertyName", "foo");
+
+        assertEquals("value1", accessor.getProperty(context, root, "dummyMap[propertyName].name"));
+    }
+
+    public void testGetAccessList() {
+        CompoundRoot root = new CompoundRoot();
+
+        SimpleAction action = new SimpleAction();
+        action.getSomeList().add("value1");
+        root.add(action);
+
+        assertEquals("value1", accessor.getProperty(new MVELContext(), root, "someList[0]"));
+    }
+
+    public void testSetList() {
+        CompoundRoot root = new CompoundRoot();
+
+        SimpleAction action = new SimpleAction();
+        root.add(action);
+
+        ArrayList someList = new ArrayList();
+        MVELContext context = new MVELContext();
+        context.put(ValueStack.REPORT_ERRORS_ON_NO_PROP, true);
+        accessor.setProperty(context, root, "someList", someList);
+
+        assertEquals(someList, action.getSomeList());
+    }
+
+    public void testSetListListener() {
+        MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING = true;
+        class MyListener implements Listener {
+            public int counter;
+            public void onEvent(Object context, String contextName, VariableResolverFactory variableFactory, Object value) {
+                counter++;
+            }
+        }
+
+        class MyBean {
+            private List someList;
+
+            public List getSomeList() {
+                return someList;
+            }
+        }
+
+        MyListener listener = new MyListener();
+        GlobalListenerFactory.registerGetListener(listener);
+        MVEL.getProperty("someList", new MyBean());
+        MVEL.getProperty("someList", new MyBean());
+        assertEquals(2, listener.counter);
+    }
+
+    public class Dummy {
+        private String name;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        private List someList;
+
+        public List getSomeList() {
+            return someList;
+        }
+
+        public void setSomeList(List someList) {
+            this.someList = someList;
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        this.accessor = new CompoundRootAccessor();
+        this.accessor.setMvelUtil(new MVELUtil());
+    }
+}

src/test/com/opensymphony/xwork2/mvel/MVELValueStackTest.java

+/*
+ * Copyright (c) 2002-2006 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork2.mvel;
+
+import com.opensymphony.xwork2.*;
+import com.opensymphony.xwork2.ognl.OgnlUtil;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.test.TestBean2;