too many chanegs to list here
git-svn-id: https://svn.apache.org/repos/asf/struts/sandbox/trunk@833573 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/BuiltinFunctionMapper.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/BuiltinFunctionMapper.java
index 7cf604a..6374928 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/BuiltinFunctionMapper.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/BuiltinFunctionMapper.java
@@ -14,6 +14,12 @@
import org.apache.commons.lang.xwork.StringUtils;
+/**
+ * <p>Builtin function availabe from UEL. Available functions are</p>
+ * <ul>
+ * <li>getText(String):Looks up a text from resource bundles using the parameter as a key</li>
+ * </ul>
+ */
public class BuiltinFunctionMapper extends FunctionMapper {
private static final Map<String, Method> BUILTIN_FUNCTIONS = new HashMap<String, Method>() {
{
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/CompoundRootELContext.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/CompoundRootELContext.java
index 89cf1a2..fa8effe 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/CompoundRootELContext.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/CompoundRootELContext.java
@@ -1,9 +1,11 @@
package org.apache.struts2.uelplugin;
+import com.opensymphony.xwork2.inject.Container;
import de.odysseus.el.util.SimpleContext;
import org.apache.struts2.uelplugin.elresolvers.CompoundRootELResolver;
-import org.apache.struts2.uelplugin.elresolvers.XWorkBeanELResolver;
import org.apache.struts2.uelplugin.elresolvers.ValueStackContextResolver;
+import org.apache.struts2.uelplugin.elresolvers.XWorkBeanELResolver;
+import org.apache.struts2.uelplugin.elresolvers.XWorkListELResolver;
import javax.el.*;
@@ -14,16 +16,17 @@
*/
public class CompoundRootELContext extends SimpleContext {
private final static BuiltinFunctionMapper BUILTIN_FUNCTION_MAPPER = new BuiltinFunctionMapper();
- public CompoundRootELContext() {
+ public CompoundRootELContext(final Container container) {
super(new CompositeELResolver() {
{
- add(new CompoundRootELResolver());
add(new ValueStackContextResolver());
+ add(new XWorkListELResolver(container));
+ add(new CompoundRootELResolver(container));
add(new ArrayELResolver(false));
- add(new ListELResolver(false));
add(new MapELResolver(false));
add(new ResourceBundleELResolver());
- add(new XWorkBeanELResolver());
+ add(new XWorkBeanELResolver(container));
+ add(new BeanELResolver());
}});
}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/JUELExtensionBuilder.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/JUELExtensionBuilder.java
index 5594997..4a2d229 100644
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/JUELExtensionBuilder.java
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/JUELExtensionBuilder.java
@@ -11,6 +11,10 @@
import de.odysseus.el.tree.impl.ast.AstIdentifier;
import org.apache.commons.lang.xwork.StringUtils;
+/**
+ * Plugs into JUEL parser to supper expressions like "#obj", to provide some level
+ * of backward compatibility with OGNL
+ */
public class JUELExtensionBuilder extends Builder {
/**
@@ -119,9 +123,3 @@
return new ExtendedParser(this, expression);
}
}
-
-class ValueStackAstIdentifier extends AstIdentifier {
- public ValueStackAstIdentifier(String name, int index) {
- super("#"+name, index);
- }
-}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelReflectionProvider.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelReflectionProvider.java
deleted file mode 100644
index 68eb94a..0000000
--- a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/UelReflectionProvider.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package org.apache.struts2.uelplugin;
-
-import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
-import com.opensymphony.xwork2.inject.Inject;
-import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
-import com.opensymphony.xwork2.util.reflection.ReflectionException;
-import org.apache.commons.beanutils.BeanUtils;
-import org.apache.commons.beanutils.PropertyUtils;
-import org.apache.commons.lang.xwork.StringUtils;
-
-import javax.el.ExpressionFactory;
-import java.lang.reflect.InvocationTargetException;
-import java.util.Map;
-
-/**
- * A OgnlReflectionProvider based on Unified EL.
- */
-public class UelReflectionProvider extends OgnlReflectionProvider {
- private ExpressionFactory factory;
- private XWorkConverter xworkConverter;
-
- @Inject
- public void setXWorkConverter(XWorkConverter conv) {
- this.xworkConverter = conv;
- }
-
-
- @Override
- public Object getValue(String expr, Map context, Object root) throws ReflectionException {
- try {
- return PropertyUtils.getProperty(root, expr);
- } catch (IllegalAccessException e) {
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
- } catch (InvocationTargetException e) {
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
- } catch (NoSuchMethodException e) {
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
- }
-
- return null;
- }
-
- @Override
- public void setValue(String expr, Map context, Object root, Object value) throws ReflectionException {
- try {
- BeanUtils.setProperty(root, expr, value);
- } catch (IllegalAccessException e) {
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
- } catch (InvocationTargetException e) {
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
- }
- }
-
- protected String wrap(String expr) {
- if (!StringUtils.startsWith(expr, "${") && !StringUtils.startsWith(expr, "#{")) {
- StringBuilder sb = new StringBuilder("${");
- sb.append(expr);
- sb.append("}");
- return sb.toString();
- } else return expr;
- }
-}
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
index bdd7b4a..5c786bf 100644
--- 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
@@ -12,7 +12,8 @@
import org.apache.struts2.uelplugin.ExpressionFactoryHolder;
/**
- * Responsible for registering the ELResolvers.
+ * Responsible for creating the ExpressionFactory that will be used by the
+ * UelValueStack
*/
public class UelServletContextListener implements ServletContextListener {
public void contextInitialized(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
index 10d872c..dac50b2 100644
--- 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
@@ -3,40 +3,49 @@
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.util.CompoundRoot;
import com.opensymphony.xwork2.util.ValueStack;
+import com.opensymphony.xwork2.util.ClearableValueStack;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.inject.Inject;
import javax.el.*;
import java.util.Map;
import java.util.TreeMap;
+import java.util.HashMap;
+
+import org.apache.struts2.uelplugin.elresolvers.AccessorsContextKey;
/**
* A ValueStack that uses Unified EL as the underlying Expression Language.
*/
-public class UelValueStack implements ValueStack {
+public class UelValueStack implements ValueStack, ClearableValueStack {
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 XWorkConverter xworkConverter;
private ELContext elContext;
+ private Container container;
+ private XWorkConverter xworkConverter;
- public UelValueStack(XWorkConverter xworkConverter) {
- this(xworkConverter, new CompoundRoot());
+ public UelValueStack(Container container) {
+ this(container, new CompoundRoot());
}
- public UelValueStack(XWorkConverter xworkConverter, ValueStack vs) {
- this(xworkConverter, new CompoundRoot(vs.getRoot()));
+ public UelValueStack(Container container, ValueStack vs) {
+ this(container, new CompoundRoot(vs.getRoot()));
}
- public UelValueStack(XWorkConverter xworkConverter, CompoundRoot root) {
- this.xworkConverter = xworkConverter;
+ public UelValueStack(Container container, CompoundRoot root) {
+ this.container = container;
+ this.xworkConverter = container.getInstance(XWorkConverter.class);
setRoot(new CompoundRoot(root));
}
+
public String findString(String expr, boolean throwException) {
return (String) findValue(expr, String.class);
}
@@ -70,6 +79,8 @@
if (expr != null && !expr.startsWith("${") && !expr.startsWith("#{")) {
expr = "#{" + expr + "}";
}
+
+ elContext.putContext(AccessorsContextKey.class, context);
elContext.putContext(XWorkConverter.class, xworkConverter);
elContext.putContext(CompoundRoot.class, root);
@@ -159,6 +170,7 @@
if (expr != null && !expr.startsWith("${") && !expr.startsWith("#{")) {
expr = "#{" + expr + "}";
}
+ elContext.putContext(AccessorsContextKey.class, context);
elContext.putContext(XWorkConverter.class, xworkConverter);
elContext.putContext(CompoundRoot.class, root);
@@ -181,6 +193,10 @@
this.context = new TreeMap();
context.put(VALUE_STACK, this);
this.root = root;
- elContext = new CompoundRootELContext();
+ 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
index 12e0c88..dca2502 100644
--- 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
@@ -31,13 +31,14 @@
public ValueStack createValueStack() {
- ValueStack results = new UelValueStack(xworkConverter);
+ ValueStack results = new UelValueStack(container);
results.getContext().put(ActionContext.CONTAINER, container);
return results;
}
public ValueStack createValueStack(ValueStack stack) {
- ValueStack results = new UelValueStack(xworkConverter, 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/AbstractResolver.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AbstractResolver.java
new file mode 100644
index 0000000..7646349
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AbstractResolver.java
@@ -0,0 +1,46 @@
+package org.apache.struts2.uelplugin.elresolvers;
+
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.conversion.NullHandler;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.ObjectFactory;
+
+import javax.el.ELResolver;
+import javax.el.ELContext;
+import java.util.Iterator;
+import java.beans.FeatureDescriptor;
+
+
+public abstract class AbstractResolver extends ELResolver {
+ protected ReflectionProvider reflectionProvider;
+ protected XWorkConverter xworkConverter;
+ protected NullHandler nullHandler;
+ protected ObjectTypeDeterminer objectTypeDeterminer;
+ protected ObjectFactory objectFactory;
+
+ public AbstractResolver(Container container) {
+ this.reflectionProvider = container.getInstance(ReflectionProvider.class);
+ this.xworkConverter = container.getInstance(XWorkConverter.class);
+ this.nullHandler = container.getInstance(NullHandler.class, "java.lang.Object");
+ this.objectTypeDeterminer = container.getInstance(ObjectTypeDeterminer.class);
+ this.objectFactory = container.getInstance(ObjectFactory.class);
+ }
+
+ public Class<?> getCommonPropertyType(ELContext elContext, Object o) {
+ return null;
+ }
+
+ public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, Object o) {
+ return null;
+ }
+
+ public Class<?> getType(ELContext elContext, Object o, Object o1) {
+ return null;
+ }
+
+ public boolean isReadOnly(ELContext elContext, Object o, Object o1) {
+ return false;
+ }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AccessorsContextKey.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AccessorsContextKey.java
new file mode 100644
index 0000000..f629cbc
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/AccessorsContextKey.java
@@ -0,0 +1,5 @@
+package org.apache.struts2.uelplugin.elresolvers;
+
+
+public interface AccessorsContextKey {
+}
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 7847acd..032eb6a 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,10 @@
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;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.xwork.StringUtils;
@@ -15,86 +18,16 @@
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Iterator;
+import java.util.Map;
/**
* An ELResolver that is capable of resolving properties against the
* CompoundRoot if available in the ELContext.
*/
-public class CompoundRootELResolver extends ELResolver {
+public class CompoundRootELResolver extends AbstractResolver {
- @Override
- public Class<?> getCommonPropertyType(ELContext context, Object base) {
- if (base == null) {
- return null;
- }
-
- return String.class;
- }
-
- @Override
- public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
- // only resolve at the root of the context
- if (base != null) {
- return null;
- }
-
- CompoundRoot root = (CompoundRoot) context.getContext(CompoundRoot.class);
- if (root == null) {
- return null;
- }
-
- ArrayList<FeatureDescriptor> list = new ArrayList<FeatureDescriptor>();
- if (root.size() > 0) {
- FeatureDescriptor descriptor = new FeatureDescriptor();
- descriptor.setValue("type", root.get(0).getClass());
- descriptor.setValue("resolvableAtDesignTime", Boolean.FALSE);
- list.add(descriptor);
- }
-
- for (Object bean : root) {
- BeanInfo info = null;
- try {
- info = Introspector.getBeanInfo(base.getClass());
- } catch (Exception ex) {
- }
- if (info != null) {
- for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
- pd.setValue("type", pd.getPropertyType());
- pd.setValue("resolvableAtDesignTime", Boolean.FALSE);
- list.add(pd);
- }
- }
- }
- return list.iterator();
- }
-
- @Override
- public Class<?> getType(ELContext context, Object base, Object property) {
- // only resolve at the root of the context
- if (base != null) {
- return null;
- }
-
- CompoundRoot root = (CompoundRoot) context.getContext(CompoundRoot.class);
- if (root == null) {
- return null;
- }
- String propertyName = (String) property;
- Object bean = findObjectForProperty(root, propertyName);
- if (bean == null) {
- return null;
- }
- try {
- Class type = determineType(bean, propertyName);
- context.setPropertyResolved(true);
- return type;
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (InvocationTargetException e) {
- throw new RuntimeException(e);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
- }
+ public CompoundRootELResolver(Container container) {
+ super(container);
}
@Override
@@ -122,29 +55,30 @@
return root.get(0);
}
- try {
- Object bean = findObjectForProperty(root, propertyName);
- if (bean != null) {
- Object retVal = PropertyUtils.getProperty(bean, propertyName);
- context.setPropertyResolved(true);
- return retVal;
+ Map<String, Object> reflectionContext = (Map) context.getContext(AccessorsContextKey.class);
+
+ Object bean = findObjectForProperty(root, propertyName);
+ if (bean != null) {
+ Object retVal = reflectionProvider.getValue(propertyName, reflectionContext, bean);
+
+ reflectionContext.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, bean.getClass());
+ reflectionContext.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, propertyName);
+
+ //if object is null, and create objects is enabled, lets do it
+ if (retVal == null && ReflectionContextState.isCreatingNullObjects(reflectionContext)) {
+ retVal = nullHandler.nullPropertyValue(reflectionContext, bean, property);
+ reflectionProvider.setValue(propertyName, reflectionContext, bean, retVal);
}
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (InvocationTargetException e) {
- throw new RuntimeException(e);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
+
+ context.setPropertyResolved(true);
+ return retVal;
}
+
return null;
}
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
-
return false;
}
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 4ebdfd7..90e5024 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
@@ -1,52 +1,76 @@
package org.apache.struts2.uelplugin.elresolvers;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.parameters.accessor.ParametersPropertyAccessor;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import com.opensymphony.xwork2.inject.Container;
import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.struts2.StrutsException;
import javax.el.BeanELResolver;
import javax.el.ELContext;
import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
-public class XWorkBeanELResolver extends BeanELResolver {
-
- public XWorkBeanELResolver() {
- super(false);
+public class XWorkBeanELResolver extends AbstractResolver {
+ public XWorkBeanELResolver(Container container) {
+ super(container);
}
- /**
- * Re-implement this to always return Object. We don't want unified EL to do
- * type conversion, we do that in the setter using xwork type conversion
- * framework.
- */
- @Override
- public Class<?> getType(ELContext context, Object base, Object property) {
- if (context == null) {
- throw new NullPointerException();
- }
+ public Object getValue(ELContext elContext, Object target, Object property) {
+ if (target != null && property != null) {
+ Map<String, Object> reflectionContext = (Map<String, Object>) elContext.getContext(AccessorsContextKey.class);
+ String propertyName = property.toString();
- if (base == null || property == null) {
- return null;
- }
+ //only handle this if there is such a property
+ if (PropertyUtils.isReadable(target, propertyName)) {
+ try {
+ Object obj = reflectionProvider.getValue(propertyName, reflectionContext, target);
- context.setPropertyResolved(true);
- return Object.class;
- }
+ reflectionContext.put(XWorkConverter.LAST_BEAN_CLASS_ACCESSED, target.getClass());
+ reflectionContext.put(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED, property.toString());
- @Override
- public void setValue(ELContext context, Object base, Object property, Object value) {
- XWorkConverter converter = (XWorkConverter) context.getContext(XWorkConverter.class);
- try {
- if (converter != null && base != null) {
- Class propType = PropertyUtils.getPropertyType(base, property.toString());
- value = converter.convertValue(null, value, propType);
+ //if object is null, and create objects is enabled, lets do it
+ if (obj == null && ReflectionContextState.isCreatingNullObjects(reflectionContext)) {
+ obj = nullHandler.nullPropertyValue(reflectionContext, target, property);
+ PropertyUtils.setProperty(target, propertyName, obj);
+ }
+
+ elContext.setPropertyResolved(true);
+ return obj;
+ } catch (Exception e) {
+ throw new StrutsException(e);
+ }
}
- super.setValue(context, base, property, value);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (InvocationTargetException e) {
- throw new RuntimeException(e);
- } catch (NoSuchMethodException e) {
- throw new RuntimeException(e);
+ }
+
+ return null;
+ }
+
+ public void setValue(ELContext elContext, Object target, Object property, Object value) {
+ if (target != null && property != null) {
+ try {
+ 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)) {
+
+ Class expectedType = reflectionProvider.getPropertyDescriptor(targetType, propertyName).getWriteMethod().getParameterTypes()[0];
+ Class valueType = value.getClass();
+
+ //convert value, if needed
+ if (!expectedType.isAssignableFrom(valueType)) {
+ value = xworkConverter.convertValue(reflectionContext, value, expectedType);
+ }
+ reflectionProvider.setValue(propertyName, reflectionContext, target, value);
+ elContext.setPropertyResolved(true);
+ }
+ } catch (Exception e) {
+ throw new StrutsException(e);
+ }
}
}
}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkListELResolver.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkListELResolver.java
new file mode 100644
index 0000000..67e6b68
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/elresolvers/XWorkListELResolver.java
@@ -0,0 +1,140 @@
+package org.apache.struts2.uelplugin.elresolvers;
+
+import com.opensymphony.xwork2.inject.Container;
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
+import com.opensymphony.xwork2.XWorkException;
+
+import javax.el.ELContext;
+import java.util.List;
+import java.util.Map;
+import java.util.Collection;
+
+public class XWorkListELResolver extends AbstractResolver {
+ public XWorkListELResolver(Container container) {
+ super(container);
+ }
+
+ public Object getValue(ELContext elContext, Object target, Object property) {
+ Map<String, Object> context = (Map) elContext.getContext(AccessorsContextKey.class);
+
+ if (target != null && property != null && target instanceof List) {
+ List list = (List) target;
+
+ Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
+ String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
+
+ Integer numericValue = null;
+ try {
+ numericValue = Integer.valueOf(property.toString());
+ } catch (NumberFormatException e) {
+ //ignore
+ }
+
+ if (numericValue != null
+ && ReflectionContextState.isCreatingNullObjects(context)
+ && objectTypeDeterminer.shouldCreateIfNew(lastClass, lastProperty, target, null, true)) {
+ int index = numericValue.intValue();
+ int listSize = list.size();
+
+ /*if (lastClass == null || lastProperty == null) {
+ return super.getProperty(context, target, name);
+ }*/
+ Class beanClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, property);
+ 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);
+ }
+
+ elContext.setPropertyResolved(true);
+ 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);
+ }
+
+ elContext.setPropertyResolved(true);
+ return result;
+ } else {
+ elContext.setPropertyResolved(true);
+ return list.get(index);
+ }
+ } else {
+ //try normal list
+ if (numericValue < list.size()) {
+ elContext.setPropertyResolved(true);
+ return list.get(numericValue);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public void setValue(ELContext elContext, Object target, Object property, Object value) {
+ Map<String, Object> context = (Map) elContext.getContext(AccessorsContextKey.class);
+ if (target != null && property != null && target instanceof List) {
+ 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, property);
+
+ if (property instanceof String && value.getClass().isArray()) {
+ // looks like the input game in the form of "someList.foo" and
+ // we are expected to define the index values ourselves.
+ // So let's do it:
+
+ Collection c = (Collection) value;
+ Object[] values = (Object[]) value;
+ for (Object v : values) {
+ try {
+ Object o = objectFactory.buildBean(convertToClass, context);
+ reflectionProvider.setProperty(property.toString(), value, target, context);
+ c.add(o);
+ } catch (Exception e) {
+ throw new XWorkException("Error converting given String values for Collection.", e);
+ }
+ }
+ }
+
+ Object realValue = getRealValue(context, value, convertToClass);
+
+ Long numericValue = null;
+ try {
+ numericValue = Long.valueOf(property.toString());
+ } catch (NumberFormatException e) {
+ //ignore
+ }
+
+ if (target instanceof List && numericValue != null) {
+ //make sure there are enough spaces in the List to set
+ List list = (List) target;
+ int listSize = list.size();
+ int count = numericValue.intValue();
+ if (count >= listSize) {
+ for (int i = listSize; i <= count; i++) {
+ list.add(null);
+ }
+ }
+
+ ((List) target).set(numericValue.intValue(), realValue);
+ }
+ }
+ }
+
+ private Object getRealValue(Map context, Object value, Class convertToClass) {
+ if (value == null || convertToClass == null) {
+ return value;
+ }
+ return xworkConverter.convertValue(context, value, convertToClass);
+ }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCache.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCache.java
new file mode 100644
index 0000000..a4929ad
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCache.java
@@ -0,0 +1,14 @@
+package org.apache.struts2.uelplugin.reflection;
+
+public interface ClassCache {
+
+ void setClassInspector(ClassCacheInspector inspector);
+
+ void clear();
+
+ int getSize();
+
+ Object get(Class key);
+
+ Object put(Class key, Object value);
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheImpl.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheImpl.java
new file mode 100644
index 0000000..4029cba
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheImpl.java
@@ -0,0 +1,100 @@
+package org.apache.struts2.uelplugin.reflection;
+
+import java.util.Arrays;
+
+/**
+ * Taken from OGNL
+ */
+public class ClassCacheImpl implements ClassCache {
+
+ /* this MUST be a power of 2 */
+ private static final int TABLE_SIZE = 512;
+ /* ...and now you see why. The table size is used as a mask for generating hashes */
+ private static final int TABLE_SIZE_MASK = TABLE_SIZE - 1;
+
+ private Entry[] _table;
+ private ClassCacheInspector _classInspector;
+ private int _size = 0;
+
+ public ClassCacheImpl() {
+ _table = new Entry[TABLE_SIZE];
+ }
+
+ public void setClassInspector(ClassCacheInspector inspector) {
+ _classInspector = inspector;
+ }
+
+ public void clear() {
+ for (int i = 0; i < _table.length; i++) {
+ _table[i] = null;
+ }
+
+ _size = 0;
+ }
+
+ public int getSize() {
+ return _size;
+ }
+
+ public final Object get(Class key) {
+ Object result = null;
+ int i = key.hashCode() & TABLE_SIZE_MASK;
+
+ for (Entry entry = _table[i]; entry != null; entry = entry.next) {
+ if (entry.key == key) {
+ result = entry.value;
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ public final Object put(Class key, Object value) {
+ if (_classInspector != null && !_classInspector.shouldCache(key))
+ return value;
+
+ Object result = null;
+ int i = key.hashCode() & TABLE_SIZE_MASK;
+ Entry entry = _table[i];
+
+ if (entry == null) {
+ _table[i] = new Entry(key, value);
+ _size++;
+ } else {
+ if (entry.key == key) {
+ result = entry.value;
+ entry.value = value;
+ } else {
+ while (true) {
+ if (entry.key == key) {
+ /* replace value */
+ result = entry.value;
+ entry.value = value;
+ break;
+ } else {
+ if (entry.next == null) {
+ /* add value */
+ entry.next = new Entry(key, value);
+ break;
+ }
+ }
+ entry = entry.next;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public String toString() {
+ return "ClassCacheImpl[" +
+ "_table=" + (_table == null ? null : Arrays.asList(_table)) +
+ '\n' +
+ ", _classInspector=" + _classInspector +
+ '\n' +
+ ", _size=" + _size +
+ '\n' +
+ ']';
+ }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheInspector.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheInspector.java
new file mode 100644
index 0000000..11dbc29
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ClassCacheInspector.java
@@ -0,0 +1,17 @@
+package org.apache.struts2.uelplugin.reflection;
+
+/**
+ * Taken from OGNL
+ */
+public interface ClassCacheInspector {
+
+ /**
+ * Invoked just before storing a class type within a cache instance.
+ *
+ * @param type
+ * The class that is to be stored.
+ *
+ * @return True if the class can be cached, false otherwise.
+ */
+ boolean shouldCache(Class type);
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/DefaultMemberAccess.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/DefaultMemberAccess.java
new file mode 100644
index 0000000..45b1e7b
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/DefaultMemberAccess.java
@@ -0,0 +1,114 @@
+package org.apache.struts2.uelplugin.reflection;
+
+import java.util.Map;
+import java.lang.reflect.Member;
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Modifier;
+
+
+/**
+ * Taken from OGNL
+ */
+public class DefaultMemberAccess implements MemberAccess
+{
+ public boolean allowPrivateAccess = false;
+ public boolean allowProtectedAccess = false;
+ public boolean allowPackageProtectedAccess = false;
+
+ /*===================================================================
+ Constructors
+ ===================================================================*/
+ public DefaultMemberAccess(boolean allowAllAccess)
+ {
+ this(allowAllAccess, allowAllAccess, allowAllAccess);
+ }
+
+ public DefaultMemberAccess(boolean allowPrivateAccess, boolean allowProtectedAccess, boolean allowPackageProtectedAccess)
+ {
+ super();
+ this.allowPrivateAccess = allowPrivateAccess;
+ this.allowProtectedAccess = allowProtectedAccess;
+ this.allowPackageProtectedAccess = allowPackageProtectedAccess;
+ }
+
+ /*===================================================================
+ Public methods
+ ===================================================================*/
+ public boolean getAllowPrivateAccess()
+ {
+ return allowPrivateAccess;
+ }
+
+ public void setAllowPrivateAccess(boolean value)
+ {
+ allowPrivateAccess = value;
+ }
+
+ public boolean getAllowProtectedAccess()
+ {
+ return allowProtectedAccess;
+ }
+
+ public void setAllowProtectedAccess(boolean value)
+ {
+ allowProtectedAccess = value;
+ }
+
+ public boolean getAllowPackageProtectedAccess()
+ {
+ return allowPackageProtectedAccess;
+ }
+
+ public void setAllowPackageProtectedAccess(boolean value)
+ {
+ allowPackageProtectedAccess = value;
+ }
+
+ /*===================================================================
+ MemberAccess interface
+ ===================================================================*/
+ public Object setup(Map context, Object target, Member member, String propertyName)
+ {
+ Object result = null;
+
+ if (isAccessible(context, target, member, propertyName)) {
+ AccessibleObject accessible = (AccessibleObject)member;
+
+ if (!accessible.isAccessible()) {
+ result = Boolean.TRUE;
+ accessible.setAccessible(true);
+ }
+ }
+ return result;
+ }
+
+ public void restore(Map context, Object target, Member member, String propertyName, Object state)
+ {
+ if (state != null) {
+ ((AccessibleObject)member).setAccessible(((Boolean)state).booleanValue());
+ }
+ }
+
+ /**
+ Returns true if the given member is accessible or can be made accessible
+ by this object.
+ */
+ public boolean isAccessible(Map context, Object target, Member member, String propertyName)
+ {
+ int modifiers = member.getModifiers();
+ boolean result = Modifier.isPublic(modifiers);
+
+ if (!result) {
+ if (Modifier.isPrivate(modifiers)) {
+ result = getAllowPrivateAccess();
+ } else {
+ if (Modifier.isProtected(modifiers)) {
+ result = getAllowProtectedAccess();
+ } else {
+ result = getAllowPackageProtectedAccess();
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/Entry.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/Entry.java
new file mode 100644
index 0000000..0aeca9f
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/Entry.java
@@ -0,0 +1,29 @@
+package org.apache.struts2.uelplugin.reflection;
+
+/**
+ * Taken from OGNL
+ */
+class Entry {
+
+ Entry next;
+ Class key;
+ Object value;
+
+ public Entry(Class key, Object value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+
+ public String toString()
+ {
+ return "Entry[" +
+ "next=" + next +
+ '\n' +
+ ", key=" + key +
+ '\n' +
+ ", value=" + value +
+ '\n' +
+ ']';
+ }
+}
\ No newline at end of file
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/GenericReflectionProvider.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/GenericReflectionProvider.java
new file mode 100644
index 0000000..f46ecc9
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/GenericReflectionProvider.java
@@ -0,0 +1,797 @@
+package org.apache.struts2.uelplugin.reflection;
+
+
+import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.util.CompoundRoot;
+import com.opensymphony.xwork2.util.logging.Logger;
+import com.opensymphony.xwork2.util.logging.LoggerFactory;
+import com.opensymphony.xwork2.util.reflection.ReflectionException;
+import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
+
+import java.beans.IndexedPropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.*;
+import java.util.*;
+
+
+/**
+ * Taken from OGNL
+ */
+public class GenericReflectionProvider implements ReflectionProvider {
+ private static final Logger LOG = LoggerFactory.getLogger(GenericReflectionProvider.class);
+
+ static final ClassCache _fieldCache = new ClassCacheImpl();
+ static final List _superclasses = new ArrayList();
+ static final ClassCache[] _declaredMethods = new ClassCache[]{new ClassCacheImpl(), new ClassCacheImpl()};
+ static final Map _primitiveTypes = new HashMap(101);
+ static final ClassCache _primitiveDefaults = new ClassCacheImpl();
+ static final Map _methodParameterTypesCache = new HashMap(101);
+ static final Map _genericMethodParameterTypesCache = new HashMap(101);
+ static final Map _ctorParameterTypesCache = new HashMap(101);
+ static final ClassCache _propertyDescriptorCache = new ClassCacheImpl();
+ static final ClassCache _staticMethodCache = new ClassCacheImpl();
+ static final ClassCache _instanceMethodCache = new ClassCacheImpl();
+ static SecurityManager _securityManager = System.getSecurityManager();
+ public static final MemberAccess DEFAULT_MEMBER_ACCESS = new DefaultMemberAccess(false);
+
+ private MemberAccess _memberAccess = DEFAULT_MEMBER_ACCESS;
+
+ public static final Object NotFound = new Object();
+ public static final List NotFoundList = new ArrayList();
+ public static final Map NotFoundMap = new HashMap();
+ public static final Object[] NoArguments = new Object[]{};
+ public static final Class[] NoArgumentTypes = new Class[]{};
+
+ private static final String SET_PREFIX = "set";
+ private static final String GET_PREFIX = "get";
+ private static final String IS_PREFIX = "is";
+
+ private static boolean _jdk15 = false;
+ private static boolean _jdkChecked = false;
+ private static boolean devMode;
+ private XWorkConverter xworkConverter;
+
+ public static int INDEXED_PROPERTY_NONE = 0;
+ public static int INDEXED_PROPERTY_INT = 1;
+ public static int INDEXED_PROPERTY_OBJECT = 2;
+ private static final Object[] EMPTY_PROPERTY_DESCRIPTORS_ARRAY = new PropertyDescriptor[0];
+
+ @Inject
+ public void setXWorkConverter(XWorkConverter conv) {
+ this.xworkConverter = conv;
+ }
+
+ @Inject("devMode")
+ public static void setDevMode(String mode) {
+ devMode = "true".equals(mode);
+ }
+
+
+ public Field getField(Class inClass, String name) {
+ Field result = null;
+
+ synchronized (_fieldCache) {
+ Object o = getFields(inClass).get(name);
+
+ if (o == null) {
+ _superclasses.clear();
+ for (Class sc = inClass; (sc != null); sc = sc.getSuperclass()) {
+ if ((o = getFields(sc).get(name)) == NotFound)
+ break;
+
+ _superclasses.add(sc);
+
+ if ((result = (Field) o) != null)
+ break;
+ }
+ /*
+ * Bubble the found value (either cache miss or actual field) to all supeclasses
+ * that we saw for quicker access next time.
+ */
+ for (int i = 0, icount = _superclasses.size(); i < icount; i++) {
+ getFields((Class) _superclasses.get(i)).put(name, (result == null) ? NotFound : result);
+ }
+ } else {
+ if (o instanceof Field) {
+ result = (Field) o;
+ } else {
+ if (result == NotFound)
+ result = null;
+ }
+ }
+ }
+ return result;
+ }
+
+ public static Map getFields(Class targetClass) {
+ Map result;
+
+ synchronized (_fieldCache) {
+ if ((result = (Map) _fieldCache.get(targetClass)) == null) {
+ Field fa[];
+
+ result = new HashMap(23);
+ fa = targetClass.getDeclaredFields();
+ for (int i = 0; i < fa.length; i++) {
+ result.put(fa[i].getName(), fa[i]);
+ }
+ _fieldCache.put(targetClass, result);
+ }
+ }
+ return result;
+ }
+
+ public Method getGetMethod(Class targetClass, String propertyName) {
+ Method result = null;
+
+
+ List methods = getDeclaredMethods(targetClass, propertyName, false /* find 'get' methods */);
+
+ if (methods != null) {
+ for (int i = 0, icount = methods.size(); i < icount; i++) {
+ Method m = (Method) methods.get(i);
+ Class[] mParameterTypes = findParameterTypes(targetClass, m); //getParameterTypes(m);
+
+ if (mParameterTypes.length == 0) {
+ result = m;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Finds the appropriate parameter types for the given {@link Method} and
+ * {@link Class} instance of the type the method is associated with. Correctly
+ * finds generic types if running in >= 1.5 jre as well.
+ *
+ * @param type The class type the method is being executed against.
+ * @param m The method to find types for.
+ * @return Array of parameter types for the given method.
+ */
+ public static Class[] findParameterTypes(Class type, Method m) {
+ if (type == null) {
+ return getParameterTypes(m);
+ }
+
+ if (!isJdk15()
+ || type.getGenericSuperclass() == null
+ || !ParameterizedType.class.isInstance(type.getGenericSuperclass())
+ || m.getDeclaringClass().getTypeParameters() == null) {
+ return getParameterTypes(m);
+ }
+
+ synchronized (_genericMethodParameterTypesCache) {
+ Class[] types;
+
+ if ((types = (Class[]) _genericMethodParameterTypesCache.get(m)) != null) {
+ return types;
+ }
+
+ ParameterizedType param = (ParameterizedType) type.getGenericSuperclass();
+ Type[] genTypes = m.getGenericParameterTypes();
+ TypeVariable[] declaredTypes = m.getDeclaringClass().getTypeParameters();
+
+ types = new Class[genTypes.length];
+
+ typeSearch:
+ for (int i = 0; i < genTypes.length; i++) {
+ TypeVariable paramType = null;
+
+ if (TypeVariable.class.isInstance(genTypes[i])) {
+ paramType = (TypeVariable) genTypes[i];
+ } else if (GenericArrayType.class.isInstance(genTypes[i])) {
+ paramType = (TypeVariable) ((GenericArrayType) genTypes[i]).getGenericComponentType();
+ } else if (Class.class.isInstance(genTypes[i])) {
+ types[i] = (Class) genTypes[i];
+ continue;
+ }
+
+ Class resolved = resolveType(param, paramType, declaredTypes);
+
+ if (resolved != null) {
+ if (GenericArrayType.class.isInstance(genTypes[i])) {
+ resolved = Array.newInstance(resolved, 0).getClass();
+ }
+
+ types[i] = resolved;
+ continue;
+ }
+
+ types[i] = m.getParameterTypes()[i];
+ }
+
+ _genericMethodParameterTypesCache.put(m, types);
+
+ return types;
+ }
+ }
+
+ static Class resolveType(ParameterizedType param, TypeVariable var, TypeVariable[] declaredTypes) {
+ if (param.getActualTypeArguments().length < 1)
+ return null;
+
+ for (int i = 0; i < declaredTypes.length; i++) {
+ if (!TypeVariable.class.isInstance(param.getActualTypeArguments()[i])
+ && declaredTypes[i].getName().equals(var.getName())) {
+ return (Class) param.getActualTypeArguments()[i];
+ }
+ }
+
+ return null;
+ }
+
+
+ /**
+ * Returns the parameter types of the given method.
+ */
+ public static Class[] getParameterTypes(Method m) {
+ synchronized (_methodParameterTypesCache) {
+ Class[] result;
+
+ if ((result = (Class[]) _methodParameterTypesCache.get(m)) == null) {
+ _methodParameterTypesCache.put(m, result = m.getParameterTypes());
+ }
+ return result;
+ }
+ }
+
+ public static List getDeclaredMethods(Class targetClass, String propertyName, boolean findSets) {
+ List result = null;
+ ClassCache cache = _declaredMethods[findSets ? 0 : 1];
+
+ synchronized (cache) {
+ Map propertyCache = (Map) cache.get(targetClass);
+
+ if ((propertyCache == null) || ((result = (List) propertyCache.get(propertyName)) == null)) {
+
+ String baseName = Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
+
+ for (Class c = targetClass; c != null; c = c.getSuperclass()) {
+ Method[] methods = c.getDeclaredMethods();
+
+ for (int i = 0; i < methods.length; i++) {
+
+ if (!isMethodCallable(methods[i]))
+ continue;
+
+ String ms = methods[i].getName();
+
+ if (ms.endsWith(baseName)) {
+ boolean isSet = false, isIs = false;
+
+ if ((isSet = ms.startsWith(SET_PREFIX)) || ms.startsWith(GET_PREFIX)
+ || (isIs = ms.startsWith(IS_PREFIX))) {
+ int prefixLength = (isIs ? 2 : 3);
+
+ if (isSet == findSets) {
+ if (baseName.length() == (ms.length() - prefixLength)) {
+ if (result == null) {
+ result = new ArrayList();
+ }
+ result.add(methods[i]);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (propertyCache == null) {
+ cache.put(targetClass, propertyCache = new HashMap(101));
+ }
+
+ propertyCache.put(propertyName, (result == null) ? NotFoundList : result);
+ }
+ return (result == NotFoundList) ? null : result;
+ }
+ }
+
+ static boolean isMethodCallable(Method m) {
+ if ((isJdk15() && m.isSynthetic()) || Modifier.isVolatile(m.getModifiers()))
+ return false;
+
+ return true;
+ }
+
+ public static boolean isJdk15() {
+ if (_jdkChecked)
+ return _jdk15;
+
+ try {
+ Class.forName("java.lang.annotation.Annotation");
+ _jdk15 = true;
+ } catch (Exception e) { /* ignore */ }
+
+ _jdkChecked = true;
+
+ return _jdk15;
+ }
+
+ public Method getSetMethod(Class targetClass, String propertyName) {
+ Method result = null;
+
+ List methods = getDeclaredMethods(targetClass, propertyName, true /* find 'set' methods */);
+
+ if (methods != null) {
+ for (int i = 0, icount = methods.size(); i < icount; i++) {
+ Method m = (Method) methods.get(i);
+ Class[] mParameterTypes = findParameterTypes(targetClass, m); //getParameterTypes(m);
+
+ if (mParameterTypes.length == 1) {
+ result = m;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ public void setProperties(Map<String, String> props, Object o, Map<String, Object> context) {
+ setProperties(props, o, context, false);
+ }
+
+ public void setProperties(Map<String, String> props, Object o, Map<String, Object> context,
+ boolean throwPropertyExceptions) throws ReflectionException {
+ for (Map.Entry<String, ?> entry : props.entrySet()) {
+ String expression = entry.getKey();
+ setProperty(expression, entry.getValue(), o, context, throwPropertyExceptions);
+ }
+ }
+
+ public void setProperties(Map<String, String> properties, Object o) {
+ setProperties(properties, o, null);
+ }
+
+ public PropertyDescriptor getPropertyDescriptor(Class targetClass, String propertyName) throws IntrospectionException, ReflectionException {
+ if (targetClass == null)
+ return null;
+
+ return (PropertyDescriptor) internalGetPropertyDescriptors(targetClass).get(propertyName);
+ }
+
+ public void copy(Object from, Object to, Map<String, Object> context,
+ Collection<String> exclusions, Collection<String> inclusions) {
+ if (from == null || to == null) {
+ LOG.warn("Attempting to copy from or to a null source. This is illegal and is bein skipped. This may be due to an error in an OGNL expression, action chaining, or some other event.");
+
+ return;
+ }
+
+ PropertyDescriptor[] fromPds;
+ PropertyDescriptor[] toPds;
+
+ try {
+ fromPds = getPropertyDescriptors(from);
+ toPds = getPropertyDescriptors(to);
+ } catch (IntrospectionException e) {
+ LOG.error("An error occured", e);
+
+ return;
+ }
+
+ Map<String, PropertyDescriptor> toPdHash = new HashMap<String, PropertyDescriptor>();
+
+ for (PropertyDescriptor toPd : toPds) {
+ toPdHash.put(toPd.getName(), toPd);
+ }
+
+ for (PropertyDescriptor fromPd : fromPds) {
+ if (fromPd.getReadMethod() != null) {
+ boolean copy = true;
+ if (exclusions != null && exclusions.contains(fromPd.getName())) {
+ copy = false;
+ } else if (inclusions != null && !inclusions.contains(fromPd.getName())) {
+ copy = false;
+ }
+
+ if (copy == true) {
+ PropertyDescriptor toPd = toPdHash.get(fromPd.getName());
+ if ((toPd != null) && (toPd.getWriteMethod() != null)) {
+ try {
+ String expr = fromPd.getName();
+ Object value = getValue(expr, context, from);
+ setValue(expr, context, to, value);
+ } catch (Exception e) {
+ // ignore, this is OK
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Looks for the real target with the specified property given a root Object which may be a
+ * CompoundRoot.
+ *
+ * @return the real target or null if no object can be found with the specified property
+ */
+ public Object getRealTarget(String property, Map<String, Object> context, Object root) {
+ //special keyword, they must be cutting the stack
+ if ("top".equals(property)) {
+ return root;
+ }
+
+ if (root instanceof CompoundRoot) {
+ // find real target
+ CompoundRoot cr = (CompoundRoot) root;
+
+ try {
+ for (Object target : cr) {
+ if (
+ hasSetProperty(context, target, property)
+ ||
+ hasGetProperty(context, target, property)
+ ||
+ getIndexedPropertyType(context, target.getClass(), property) != INDEXED_PROPERTY_NONE
+ ) {
+ return target;
+ }
+ }
+ } catch (IntrospectionException ex) {
+ throw new ReflectionException("Cannot figure out real target class", ex);
+ }
+
+ return null;
+ }
+
+ return root;
+ }
+
+ /**
+ * Determines the index property type, if any. Returns <code>INDEXED_PROPERTY_NONE</code> if
+ * the property is not index-accessible as determined by OGNL or JavaBeans. If it is indexable
+ * then this will return whether it is a JavaBeans indexed property, conforming to the indexed
+ * property patterns (returns <code>INDEXED_PROPERTY_INT</code>) or if it conforms to the
+ * OGNL arbitrary object indexable (returns <code>INDEXED_PROPERTY_OBJECT</code>).
+ */
+ public int getIndexedPropertyType(Map<String, Object> context, Class sourceClass, String name) {
+ int result = INDEXED_PROPERTY_NONE;
+
+ try {
+ PropertyDescriptor pd = getPropertyDescriptor(sourceClass, name);
+ if (pd != null) {
+ if (pd instanceof IndexedPropertyDescriptor) {
+ result = INDEXED_PROPERTY_INT;
+ } else {
+ if (pd instanceof ognl.ObjectIndexedPropertyDescriptor) {
+ result = INDEXED_PROPERTY_OBJECT;
+ }
+ }
+ }
+ } catch (Exception ex) {
+ throw new ReflectionException("problem determining if '" + name + "' is an indexed property", ex);
+ }
+ return result;
+ }
+
+ public final boolean hasSetProperty(Map<String, Object> context, Object target, Object oname)
+ throws IntrospectionException {
+ Class targetClass = (target == null) ? null : target.getClass();
+ String name = oname.toString();
+
+ return hasSetMethod(context, target, targetClass, name) || hasField(context, target, targetClass, name);
+ }
+
+ public final boolean hasSetMethod(Map<String, Object> context, Object target, Class targetClass, String propertyName)
+ throws IntrospectionException {
+ return isMethodAccessible(context, target, getSetMethod(targetClass, propertyName), propertyName);
+ }
+
+ public boolean isMethodAccessible(Map<String, Object> context, Object target, Method method, String propertyName) {
+ return (method != null) && _memberAccess.isAccessible(context, target, method, propertyName);
+ }
+
+ public final boolean hasGetProperty(Map<String, Object> context, Object target, Object oname)
+ throws IntrospectionException {
+ Class targetClass = (target == null) ? null : target.getClass();
+ String name = oname.toString();
+
+ return hasGetMethod(context, target, targetClass, name) || hasField(context, target, targetClass, name);
+ }
+
+ public boolean hasGetMethod(Map<String, Object> context, Object target, Class targetClass, String propertyName)
+ throws IntrospectionException {
+ return isMethodAccessible(context, target, getGetMethod(targetClass, propertyName), propertyName);
+ }
+
+ public boolean hasField(Map<String, Object> context, Object target, Class inClass, String propertyName) {
+ Field f = getField(inClass, propertyName);
+
+ return (f != null) && isFieldAccessible(context, target, f, propertyName);
+ }
+
+ public boolean isFieldAccessible(Map<String, Object> context, Object target, Class inClass, String propertyName) {
+ return isFieldAccessible(context, target, getField(inClass, propertyName), propertyName);
+ }
+
+ public boolean isFieldAccessible(Map<String, Object> context, Object target, Field field, String propertyName) {
+ return _memberAccess.isAccessible(context, target, field, propertyName);
+ }
+
+
+ public void setProperty(String name, Object value, Object o, Map<String, Object> context) {
+ setProperty(name, value, o, context, false);
+ }
+
+ public void setProperty(String name, Object value, Object o, Map<String, Object> context, boolean throwPropertyExceptions) {
+ try {
+ setValue(name, context, o, value);
+ } catch (Exception e) {
+ String msg = "Caught an Exception while setting property '" + name + "' on type '" + o.getClass().getName() + "'.";
+
+ if (throwPropertyExceptions) {
+ throw new ReflectionException(msg, e);
+ } else {
+ if (devMode) {
+ LOG.warn(msg, e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a Map with read properties for the given source object.
+ * <p/>
+ * If the source object does not have a read property (i.e. write-only) then
+ * the property is added to the map with the value <code>here is no read method for property-name</code>.
+ *
+ * @param source the source object.
+ * @return a Map with (key = read property name, value = value of read property).
+ * @throws IntrospectionException is thrown if an exception occurs during introspection.
+ */
+ public Map getBeanMap(Object source) throws IntrospectionException {
+ Map beanMap = new HashMap();
+ PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(source);
+ for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
+ String propertyName = propertyDescriptor.getDisplayName();
+ Method readMethod = propertyDescriptor.getReadMethod();
+ if (readMethod != null) {
+ Object value = getValue(propertyName, Collections.EMPTY_MAP, source);
+ beanMap.put(propertyName, value);
+ } else {
+ beanMap.put(propertyName, "There is no read method for " + propertyName);
+ }
+ }
+ return beanMap;
+ }
+
+ public Object getValue(String expression, Map<String, Object> context, Object root)
+ throws ReflectionException {
+ Method method = getGetMethod(root.getClass(), expression);
+
+ if (method != null) {
+ try {
+ return method.invoke(root, NoArguments);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ throw new ReflectionException("Unable to find get method for [" + expression + "]");
+ }
+
+ }
+
+ public void setValue(String expression, Map<String, Object> context, Object root,
+ Object value) throws ReflectionException {
+ Method method = getSetMethod(root.getClass(), expression);
+ if (method != null) {
+ if (value != null) {
+ //convert type if needed
+ Class expectedType = method.getParameterTypes()[0];
+ Class valueType = value.getClass();
+ if (!expectedType.isAssignableFrom(valueType)) {
+ value = xworkConverter.convertValue(context, value, expectedType);
+ }
+ }
+
+ try {
+ method.invoke(root, value);
+ } catch (Exception e) {
+ throw new ReflectionException(e);
+ }
+ } else {
+ throw new ReflectionException("Unable to find set method for [" + expression + "]");
+ }
+ }
+
+ public Map internalGetPropertyDescriptors(Class targetClass) throws IntrospectionException {
+ Map result;
+
+ synchronized (_propertyDescriptorCache) {
+ if ((result = (Map) _propertyDescriptorCache.get(targetClass)) == null) {
+ PropertyDescriptor[] pda = Introspector.getBeanInfo(targetClass).getPropertyDescriptors();
+
+ result = new HashMap(101);
+ for (int i = 0, icount = pda.length; i < icount; i++) {
+ // workaround for Introspector bug 6528714 (bugs.sun.com)
+ if (pda[i].getReadMethod() != null && !isMethodCallable(pda[i].getReadMethod())) {
+ pda[i].setReadMethod(findClosestMatchingMethod(targetClass, pda[i].getReadMethod(), pda[i].getName(),
+ pda[i].getPropertyType(), true));
+ }
+ if (pda[i].getWriteMethod() != null && !isMethodCallable(pda[i].getWriteMethod())) {
+ pda[i].setWriteMethod(findClosestMatchingMethod(targetClass, pda[i].getWriteMethod(), pda[i].getName(),
+ pda[i].getPropertyType(), false));
+ }
+
+ result.put(pda[i].getName(), pda[i]);
+ }
+
+ findObjectIndexedPropertyDescriptors(targetClass, result);
+ _propertyDescriptorCache.put(targetClass, result);
+ }
+ }
+
+ return result;
+ }
+
+ static Method findClosestMatchingMethod(Class targetClass, Method m, String propertyName,
+ Class propertyType, boolean isReadMethod) {
+ List methods = getDeclaredMethods(targetClass, propertyName, !isReadMethod);
+
+ for (int i = 0; i < methods.size(); i++) {
+ Method method = (Method) methods.get(i);
+
+ if (method.getName().equals(m.getName())
+ && m.getReturnType().isAssignableFrom(m.getReturnType())
+ && method.getReturnType() == propertyType
+ && method.getParameterTypes().length == m.getParameterTypes().length) {
+ return method;
+ }
+ }
+
+ return m;
+ }
+
+ static void findObjectIndexedPropertyDescriptors(Class targetClass, Map intoMap)
+ throws ReflectionException {
+ Map allMethods = getMethods(targetClass, false);
+ Map pairs = new HashMap(101);
+
+ for (Iterator it = allMethods.keySet().iterator(); it.hasNext();) {
+ String methodName = (String) it.next();
+ List methods = (List) allMethods.get(methodName);
+
+ /*
+ * Only process set/get where there is exactly one implementation of the method per
+ * class and those implementations are all the same
+ */
+ if (indexMethodCheck(methods)) {
+ boolean isGet = false, isSet = false;
+ Method m = (Method) methods.get(0);
+
+ if (((isSet = methodName.startsWith(SET_PREFIX)) || (isGet = methodName.startsWith(GET_PREFIX)))
+ && (methodName.length() > 3)) {
+ String propertyName = Introspector.decapitalize(methodName.substring(3));
+ Class[] parameterTypes = getParameterTypes(m);
+ int parameterCount = parameterTypes.length;
+
+ if (isGet && (parameterCount == 1) && (m.getReturnType() != Void.TYPE)) {
+ List pair = (List) pairs.get(propertyName);
+
+ if (pair == null) {
+ pairs.put(propertyName, pair = new ArrayList());
+ }
+ pair.add(m);
+ }
+ if (isSet && (parameterCount == 2) && (m.getReturnType() == Void.TYPE)) {
+ List pair = (List) pairs.get(propertyName);
+
+ if (pair == null) {
+ pairs.put(propertyName, pair = new ArrayList());
+ }
+ pair.add(m);
+ }
+ }
+ }
+ }
+
+ for (Iterator it = pairs.keySet().iterator(); it.hasNext();) {
+ String propertyName = (String) it.next();
+ List methods = (List) pairs.get(propertyName);
+
+ if (methods.size() == 2) {
+ Method method1 = (Method) methods.get(0), method2 = (Method) methods.get(1), setMethod = (method1
+ .getParameterTypes().length == 2) ? method1 : method2, getMethod = (setMethod == method1) ? method2
+ : method1;
+ Class keyType = getMethod.getParameterTypes()[0], propertyType = getMethod.getReturnType();
+
+ if (keyType == setMethod.getParameterTypes()[0]) {
+ if (propertyType == setMethod.getParameterTypes()[1]) {
+ ObjectIndexedPropertyDescriptor propertyDescriptor;
+
+ try {
+ propertyDescriptor = new ObjectIndexedPropertyDescriptor(propertyName, propertyType,
+ getMethod, setMethod);
+ } catch (Exception ex) {
+ throw new ReflectionException("creating object indexed property descriptor for '" + propertyName
+ + "' in " + targetClass, ex);
+ }
+ intoMap.put(propertyName, propertyDescriptor);
+ }
+ }
+
+ }
+ }
+ }
+
+ public static Map getMethods(Class targetClass, boolean staticMethods) {
+ ClassCache cache = (staticMethods ? _staticMethodCache : _instanceMethodCache);
+ Map result;
+
+ synchronized (cache) {
+ if ((result = (Map) cache.get(targetClass)) == null) {
+ cache.put(targetClass, result = new HashMap(23));
+
+ for (Class c = targetClass; c != null; c = c.getSuperclass()) {
+ Method[] ma = c.getDeclaredMethods();
+
+ for (int i = 0, icount = ma.length; i < icount; i++) {
+ // skip over synthetic methods
+
+ if (!isMethodCallable(ma[i]))
+ continue;
+
+ if (Modifier.isStatic(ma[i].getModifiers()) == staticMethods) {
+ List ml = (List) result.get(ma[i].getName());
+
+ if (ml == null)
+ result.put(ma[i].getName(), ml = new ArrayList());
+
+ ml.add(ma[i]);
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private static final boolean indexMethodCheck(List methods) {
+ boolean result = false;
+
+ if (methods.size() > 0) {
+ Method fm = (Method) methods.get(0);
+ Class[] fmpt = getParameterTypes(fm);
+ int fmpc = fmpt.length;
+ Class lastMethodClass = fm.getDeclaringClass();
+
+ result = true;
+ for (int i = 1; result && (i < methods.size()); i++) {
+ Method m = (Method) methods.get(i);
+ Class c = m.getDeclaringClass();
+
+ // Check to see if more than one method implemented per class
+ if (lastMethodClass == c) {
+ result = false;
+ } else {
+ Class[] mpt = getParameterTypes(fm);
+ int mpc = fmpt.length;
+
+ if (fmpc != mpc) {
+ result = false;
+ }
+ for (int j = 0; j < fmpc; j++) {
+ if (fmpt[j] != mpt[j]) {
+ result = false;
+ break;
+ }
+ }
+ }
+ lastMethodClass = c;
+ }
+ }
+ return result;
+ }
+
+ public PropertyDescriptor[] getPropertyDescriptors(Object source) throws IntrospectionException {
+ return (PropertyDescriptor[]) internalGetPropertyDescriptors(source.getClass()).values().toArray(EMPTY_PROPERTY_DESCRIPTORS_ARRAY);
+ }
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/MemberAccess.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/MemberAccess.java
new file mode 100644
index 0000000..1f9301f
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/MemberAccess.java
@@ -0,0 +1,26 @@
+package org.apache.struts2.uelplugin.reflection;
+
+import java.util.Map;
+import java.lang.reflect.Member;
+
+/**
+ * Taken from OGNL
+ */
+public interface MemberAccess
+{
+ /**
+ Sets the member up for accessibility
+ */
+ public Object setup(Map context, Object target, Member member, String propertyName);
+
+ /**
+ Restores the member from the previous setup call.
+ */
+ public void restore(Map context, Object target, Member member, String propertyName, Object state);
+
+ /**
+ Returns true if the given member is accessible or can be made accessible
+ by this object.
+ */
+ public boolean isAccessible(Map context, Object target, Member member, String propertyName);
+}
diff --git a/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ObjectIndexedPropertyDescriptor.java b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ObjectIndexedPropertyDescriptor.java
new file mode 100644
index 0000000..c12a72b
--- /dev/null
+++ b/struts2-uel-plugin/src/main/java/org/apache/struts2/uelplugin/reflection/ObjectIndexedPropertyDescriptor.java
@@ -0,0 +1,39 @@
+package org.apache.struts2.uelplugin.reflection;
+
+import java.beans.PropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.lang.reflect.Method;
+
+/**
+ * Taken from OGNL
+ */
+public class ObjectIndexedPropertyDescriptor extends PropertyDescriptor
+{
+ private Method indexedReadMethod;
+ private Method indexedWriteMethod;
+ private Class propertyType;
+
+ public ObjectIndexedPropertyDescriptor(String propertyName, Class propertyType, Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException
+ {
+ super(propertyName, null, null);
+ this.propertyType = propertyType;
+ this.indexedReadMethod = indexedReadMethod;
+ this.indexedWriteMethod = indexedWriteMethod;
+ }
+
+ public Method getIndexedReadMethod()
+ {
+ return indexedReadMethod;
+ }
+
+ public Method getIndexedWriteMethod()
+ {
+ return indexedWriteMethod;
+ }
+
+ public Class getPropertyType()
+ {
+ return propertyType;
+ }
+}
+
diff --git a/struts2-uel-plugin/src/main/resources/struts-plugin.xml b/struts2-uel-plugin/src/main/resources/struts-plugin.xml
index 5147ebe..89314f6 100644
--- a/struts2-uel-plugin/src/main/resources/struts-plugin.xml
+++ b/struts2-uel-plugin/src/main/resources/struts-plugin.xml
@@ -8,7 +8,7 @@
<bean type="com.opensymphony.xwork2.util.ValueStackFactory" name="uel"
class="org.apache.struts2.uelplugin.UelValueStackFactory"/>
<bean type="com.opensymphony.xwork2.util.reflection.ReflectionProvider" name="uel"
- class="org.apache.struts2.uelplugin.UelReflectionProvider"/>
+ class="org.apache.struts2.uelplugin.reflection.GenericReflectionProvider"/>
<bean type="com.opensymphony.xwork2.util.reflection.ReflectionContextFactory" name="uel"
class="org.apache.struts2.uelplugin.UelReflectionContextFactory"/>
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/AbstractUelBaseTest.java
new file mode 100644
index 0000000..15630f1
--- /dev/null
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/AbstractUelBaseTest.java
@@ -0,0 +1,89 @@
+package org.apache.struts2.uelplugin;
+
+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;
+import org.springframework.mock.web.MockServletContext;
+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;
+import java.util.Map;
+import java.text.ParseException;
+import java.text.DateFormat;
+
+
+public abstract class AbstractUelBaseTest extends XWorkTestCase {
+ private ExpressionFactory factory = ExpressionFactory.newInstance();
+ protected XWorkConverter converter;
+ protected CompoundRoot root;
+ protected UelValueStack stack;
+ protected DateFormat format = DateFormat.getDateInstance();
+ protected ReflectionProvider reflectionProvider;
+
+ private class DateConverter extends StrutsTypeConverter {
+
+ @Override
+ public Object convertFromString(Map context, String[] values, Class toClass) {
+ try {
+ return format.parseObject(values[0]);
+ } catch (ParseException e) {
+ return null;
+ }
+ }
+
+ @Override
+ public String convertToString(Map context, Object o) {
+ return format.format(o);
+ }
+
+ }
+
+
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ 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(ReflectionProvider.class, GenericReflectionProvider.class);
+ }
+ });
+
+ converter = container.getInstance(XWorkConverter.class);
+ reflectionProvider = container.getInstance(ReflectionProvider.class);
+ converter.registerConverter("java.util.Date", new DateConverter());
+ this.root = new CompoundRoot();
+ this.stack = new UelValueStack(container);
+ stack.setRoot(root);
+ stack.getContext().put(ActionContext.CONTAINER, container);
+
+ MockServletContext servletContext = new MockServletContext();
+ ActionContext context = new ActionContext(stack.getContext());
+ ActionContext.setContext(context);
+ ServletActionContext.setServletContext(servletContext);
+
+ //simulate start up
+ 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 14e3df7..9be211f 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
@@ -15,11 +15,7 @@
import javax.servlet.ServletContextEvent;
-public class BuiltinFunctionsTest extends XWorkTestCase {
- private XWorkConverter converter;
- private CompoundRoot root;
- private UelValueStack stack;
-
+public class BuiltinFunctionsTest extends AbstractUelBaseTest {
public void testGetText() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
TestAction action = new TestAction();
stack.push(action);
@@ -27,25 +23,4 @@
assertEquals("This is the key!", stack.findValue("${getText('key')}"));
}
-
-
-
- protected void setUp() throws Exception {
- super.setUp();
-
- converter = container.getInstance(XWorkConverter.class);
- this.root = new CompoundRoot();
- this.stack = new UelValueStack(converter);
- stack.setRoot(root);
- stack.getContext().put(ActionContext.CONTAINER, container);
-
- MockServletContext servletContext = new MockServletContext();
- ActionContext context = new ActionContext(stack.getContext());
- ActionContext.setContext(context);
- ServletActionContext.setServletContext(servletContext);
-
- //simulate start up
- UelServletContextListener listener = new UelServletContextListener();
- listener.contextInitialized(new ServletContextEvent(servletContext));
- }
}
diff --git a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/DummyTypeConverter.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/DummyTypeConverter.java
new file mode 100644
index 0000000..73be98d
--- /dev/null
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/DummyTypeConverter.java
@@ -0,0 +1,18 @@
+package org.apache.struts2.uelplugin;
+
+import com.opensymphony.xwork2.conversion.TypeConverter;
+
+import java.util.Map;
+import java.lang.reflect.Member;
+
+import org.apache.struts2.util.StrutsTypeConverter;
+
+public class DummyTypeConverter extends StrutsTypeConverter {
+ public Object convertFromString(Map context, String[] values, Class toClass) {
+ return "converted";
+ }
+
+ public String convertToString(Map context, Object o) {
+ return "converted-tostring";
+ }
+}
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/JuelMethodInvocationTest.java
index 44e3a93..9ed8fb0 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/JuelMethodInvocationTest.java
@@ -13,11 +13,7 @@
import java.util.HashMap;
-public class JuelMethodInvocationTest extends XWorkTestCase {
- private XWorkConverter converter;
- private CompoundRoot root;
- private UelValueStack stack;
-
+public class JuelMethodInvocationTest extends AbstractUelBaseTest {
public void testBasicMethods() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
assertEquals("text", stack.findValue("${' text '.trim()}"));
assertEquals(3, stack.findValue("${'123'.length()}"));
@@ -28,28 +24,9 @@
assertEquals("123456", stack.findValue("${'123'.concat('456')}"));
}
- public void testMethodsWithParamsAndContextReference() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
- stack.getContext().put("s0", "Lex");
- stack.getContext().put("s1", "Luthor");
- assertEquals("Lex Luthor", stack.findValue("${#s0.concat(' ').concat(#s1)}"));
- }
-
- protected void setUp() throws Exception {
- super.setUp();
-
- converter = container.getInstance(XWorkConverter.class);
- this.root = new CompoundRoot();
- this.stack = new UelValueStack(converter);
- stack.setRoot(root);
- stack.getContext().put(ActionContext.CONTAINER, container);
-
- MockServletContext servletContext = new MockServletContext();
- ActionContext context = new ActionContext(stack.getContext());
- ActionContext.setContext(context);
- ServletActionContext.setServletContext(servletContext);
-
- //simulate start up
- UelServletContextListener listener = new UelServletContextListener();
- listener.contextInitialized(new ServletContextEvent(servletContext));
+ public void testMethodsWithParamsAndContextReference() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ stack.getContext().put("s0", "Lex");
+ stack.getContext().put("s1", "Luthor");
+ assertEquals("Lex Luthor", stack.findValue("${#s0.concat(' ').concat(#s1)}"));
}
}
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
new file mode 100644
index 0000000..886b365
--- /dev/null
+++ b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ParametersTest.java
@@ -0,0 +1,85 @@
+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.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 testAnnotatedTypeConverter() {
+ TestAction action = new TestAction();
+ assertNull(action.getConverted());
+ root.push(action);
+
+ stack.setValue("converted", "someval");
+ assertEquals("converted", action.getConverted());
+ }
+
+
+ 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/ReflectionProviderTest.java b/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ReflectionProviderTest.java
deleted file mode 100644
index 8fa8fe2..0000000
--- a/struts2-uel-plugin/src/test/java/org/apache/struts2/uelplugin/ReflectionProviderTest.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.apache.struts2.uelplugin;
-
-import com.opensymphony.xwork2.XWorkTestCase;
-import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
-
-import java.util.HashMap;
-
-public class ReflectionProviderTest extends XWorkTestCase {
- private ReflectionProvider reflectionProvider;
-
- public void testGetSimple() {
- TestObject obj = new TestObject();
- TestObject obj2 = new TestObject();
- obj2.setAge(100);
- obj.setInner(obj2);
-
- assertSame(obj2, reflectionProvider.getValue("inner", new HashMap(), obj));
- assertEquals(100, reflectionProvider.getValue("inner.age", new HashMap(), obj));
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- reflectionProvider = new UelReflectionProvider();
- container.inject(reflectionProvider);
- }
-}
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 10226c1..03b06de 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
@@ -1,6 +1,30 @@
package org.apache.struts2.uelplugin;
import com.opensymphony.xwork2.ActionSupport;
+import com.opensymphony.xwork2.conversion.annotations.Conversion;
+import com.opensymphony.xwork2.conversion.annotations.ConversionType;
+import com.opensymphony.xwork2.conversion.annotations.TypeConversion;
+@Conversion
public class TestAction extends ActionSupport {
+ private TestObject object;
+
+ private String converted;
+
+ @TypeConversion(type = ConversionType.APPLICATION, converter = "org.apache.struts2.uelplugin.DummyTypeConverter")
+ public String getConverted() {
+ return converted;
+ }
+
+ public void setConverted(String converted) {
+ this.converted = converted;
+ }
+
+ public TestObject getObject() {
+ return object;
+ }
+
+ public void setObject(TestObject object) {
+ this.object = object;
+ }
}
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 5583f89..e0517fa 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
@@ -2,6 +2,7 @@
import java.util.Date;
import java.util.Map;
+import java.util.List;
public class TestObject {
@@ -11,6 +12,24 @@
private TestObject inner;
private Map parameters;
private Object object;
+ private List list;
+ private List<TestObject> typedList;
+
+ public List<TestObject> getTypedList() {
+ return typedList;
+ }
+
+ public void setTypedList(List<TestObject> typedList) {
+ this.typedList = typedList;
+ }
+
+ public List getList() {
+ return list;
+ }
+
+ public void setList(List list) {
+ this.list = list;
+ }
public TestObject() {
}
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/UelTest.java
index fd63336..28fabec 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/UelTest.java
@@ -2,7 +2,21 @@
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;
@@ -16,49 +30,17 @@
import java.text.ParseException;
import java.util.*;
-public class UelTest extends XWorkTestCase {
- private ExpressionFactory factory = ExpressionFactory.newInstance();
- private XWorkConverter converter;
- private DateFormat format = DateFormat.getDateInstance();
- private CompoundRoot root;
- private UelValueStack stack;
+public class UelTest extends AbstractUelBaseTest {
- private class DateConverter extends StrutsTypeConverter {
+ public void testReadList() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
+ TestObject obj = new TestObject();
+ root.push(obj);
- @Override
- public Object convertFromString(Map context, String[] values, Class toClass) {
- try {
- return format.parseObject(values[0]);
- } catch (ParseException e) {
- return null;
- }
- }
-
- @Override
- public String convertToString(Map context, Object o) {
- return format.format(o);
- }
-
- }
-
- protected void setUp() throws Exception {
- super.setUp();
-
- converter = container.getInstance(XWorkConverter.class);
- converter.registerConverter("java.util.Date", new DateConverter());
- this.root = new CompoundRoot();
- this.stack = new UelValueStack(converter);
- stack.setRoot(root);
- stack.getContext().put(ActionContext.CONTAINER, container);
-
- MockServletContext servletContext = new MockServletContext();
- ActionContext context = new ActionContext(stack.getContext());
- ActionContext.setContext(context);
- ServletActionContext.setServletContext(servletContext);
-
- //simulate start up
- UelServletContextListener listener = new UelServletContextListener();
- listener.contextInitialized(new ServletContextEvent(servletContext));
+ //list
+ List someList = new ArrayList(3);
+ obj.setObject(someList);
+ someList.add(10);
+ assertEquals(10, stack.findValue("object[0]"));
}