Commits

Anonymous committed cf88173

Improve parameter binding

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@2070e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (7)

core/src/main/java/com/opensymphony/xwork2/interceptor/ParametersInterceptor.java

 import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
 import com.opensymphony.xwork2.inject.Inject;
+import com.opensymphony.xwork2.inject.Container;
 import com.opensymphony.xwork2.util.*;
 import com.opensymphony.xwork2.util.logging.Logger;
 import com.opensymphony.xwork2.util.logging.LoggerFactory;
 
     private ValueStackFactory valueStackFactory;
 
-    //only used if enableSimpleParametersBinder is true
-    private XWorkParametersBinder parametersBinder;
     private boolean enableSimpleParametersBinder = true;
+    private Container container;
 
     @Inject
     public void setValueStackFactory(ValueStackFactory valueStackFactory) {
         this.valueStackFactory = valueStackFactory;
     }
 
+    @Inject
+    public void setValueStackFactory(Container container) {
+        this.container = container;
+    }
+
     @Inject("devMode")
     public static void setDevMode(String mode) {
         devMode = "true".equals(mode);
         this.enableSimpleParametersBinder = "true".equals(simpleBinder);
     }
 
-    @Inject
-    public void setParametersBinder(XWorkParametersBinder parametersBinder) {
-        this.parametersBinder = parametersBinder;
-    }
-
     public void setAcceptParamNames(String commaDelim) {
         Collection<String> acceptPatterns = asCollection(commaDelim);
         if (acceptPatterns != null) {
 
         Map<String, Object> newContext = newStack.getContext();
         CompoundRoot stackRoot = newStack.getRoot();
+        XWorkParametersBinder parametersBinder = container.getInstance(XWorkParametersBinder.class);
+        
         for (Map.Entry<String, Object> entry : acceptableParameters.entrySet()) {
             String name = entry.getKey();
             Object value = entry.getValue();
             try {
-                if (enableSimpleParametersBinder)
+                if (enableSimpleParametersBinder) {
                     parametersBinder.setProperty(newContext, stackRoot, name, value);
-                else
+                } else
                     newStack.setValue(name, value);
             } catch (RuntimeException e) {
                 if (devMode) {

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

 
     public void setProperty(Map<String, Object> context, Object action, String paramName, Object paramValue, boolean throwExceptionOnFailure) {
         try {
+            paramName = cleanupExpression(paramName);
             context.put(ValueStack.REPORT_ERRORS_ON_NO_PROP, (throwExceptionOnFailure) ? Boolean.TRUE : Boolean.FALSE);
 
             List<Node> nodes = nodesCache.get(paramName);
         }
     }
 
+    protected String cleanupExpression(String expr) {
+        if ((StringUtils.startsWith(expr, "#{") || StringUtils.startsWith(expr, "${"))
+                && StringUtils.endsWith(expr, "}"))
+            return expr.substring(2, expr.length() - 1);
+        else
+            return expr;
+    }
+
     protected ParametersPropertyAccessor getPropertyAccessor(Object object) {
         if (object instanceof CompoundRoot)
             return compoundAccessor;

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

 
                 try {
                     if ((reflectionProvider.getGetMethod(o.getClass(), name.toString()) != null) || ((o instanceof Map) && ((Map) o).containsKey(name))) {
-                        return reflectionProvider.getValue(name.toString(), context, root);
+                        return reflectionProvider.getValue(name.toString(), context, o);
                     }
                 } catch (Exception e) {
                     final String msg = "Caught an exception while getting property " + name;

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

 
 
 import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
+import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
 import com.opensymphony.xwork2.inject.Inject;
 import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
 import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
 import com.opensymphony.xwork2.parameters.bytecode.AccessorBytecodeUtil;
 import com.opensymphony.xwork2.parameters.bytecode.Getter;
 import com.opensymphony.xwork2.parameters.bytecode.Setter;
+import com.opensymphony.xwork2.ObjectFactory;
 
 import java.util.Map;
 
  */
 public class ParametersObjectPropertyAccessor implements ParametersPropertyAccessor {
     protected AccessorBytecodeUtil accessorBytecodeUtil;
+    private XWorkConverter xworkConverter;
+    private ReflectionProvider reflectionProvider;
+
+    @Inject
+    public void setXWorkConverter(XWorkConverter conv) {
+        this.xworkConverter = conv;
+    }
+
+    @Inject
+    public void setReflectionProvider(ReflectionProvider reflectionProvider) {
+        this.reflectionProvider = reflectionProvider;
+    }
+
 
     @Override
     public Object getProperty(Map context, Object target, Object property) throws Exception {
 
     @Override
     public void setProperty(Map context, Object target, Object property, Object value) throws Exception {
-        Setter setter = accessorBytecodeUtil.getSetter(target.getClass(), value.getClass(), property.toString());
+        String propertyName = property.toString();
+        Class targetType = target.getClass();
+
+        Class expectedType = reflectionProvider.getPropertyDescriptor(targetType, propertyName).getWriteMethod().getParameterTypes()[0];
+        Class valueType = value.getClass();
+
+        Setter setter = accessorBytecodeUtil.getSetter(targetType, expectedType, propertyName);
+
+        //convert value, if needed
+        if (!expectedType.isAssignableFrom(valueType)) {
+            value = xworkConverter.convertValue(context, value, expectedType);
+        }
+
         setter.invoke(target, value);
     }
 

core/src/main/java/com/opensymphony/xwork2/parameters/bytecode/AccessorBytecodeUtil.java

         cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, className, null, "java/lang/Object", new String[]{SETTER_INTERFACE});
 
         {
+            fv = cw.visitField(ACC_PRIVATE, "propertyClass", "Ljava/lang/Class;", null, null);
+            fv.visitEnd();
+        }
+        {
             fv = cw.visitField(ACC_PRIVATE, "propertyName", "Ljava/lang/String;", null, null);
             fv.visitEnd();
         }
         {
-            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/String;)V", null, null);
+            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V", null, null);
             mv.visitCode();
             mv.visitVarInsn(ALOAD, 0);
             mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
             mv.visitVarInsn(ALOAD, 0);
             mv.visitVarInsn(ALOAD, 1);
             mv.visitFieldInsn(PUTFIELD, className, "propertyName", "Ljava/lang/String;");
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitVarInsn(ALOAD, 2);
+            mv.visitFieldInsn(PUTFIELD, className, "propertyClass", "Ljava/lang/Class;");
             mv.visitInsn(RETURN);
-            mv.visitMaxs(2, 2);
+            mv.visitMaxs(2, 3);
             mv.visitEnd();
         }
         {
             mv.visitMaxs(1, 1);
             mv.visitEnd();
         }
+        {
+            mv = cw.visitMethod(ACC_PUBLIC, "getPropertyClass", "()Ljava/lang/Class;", null, null);
+            mv.visitCode();
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(GETFIELD, className, "propertyClass", "Ljava/lang/Class;");
+            mv.visitInsn(ARETURN);
+            mv.visitMaxs(1, 1);
+            mv.visitEnd();
+        }
         cw.visitEnd();
 
         //this one needs "." instead of "/" in the name
         String finalClassName = targetType.getName() + postfix;
 
         Class clazz = classLoader.defineClass(finalClassName, cw.toByteArray());
-        Constructor constructor = clazz.getConstructor(new Class[]{String.class});
-        Setter setter = (Setter) constructor.newInstance(new Object[]{propertyName});
+        Constructor constructor = clazz.getConstructor(new Class[]{String.class, Class.class});
+        Setter setter = (Setter) constructor.newInstance(new Object[]{propertyName, valueType});
         settersCache.put(key, setter);
         return setter;
     }

core/src/main/java/com/opensymphony/xwork2/parameters/bytecode/Setter.java

 public interface Setter {
     void invoke(Object target, Object param);
     String getPropertyName();
+    Class getPropertyClass();
 }

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

 import java.util.Map;
 
 import ognl.OgnlException;
+import org.objectweb.asm.util.ASMifierClassVisitor;
 
 public class XWorkParametersBinderTest extends XWorkTestCase {
     private XWorkParametersBinder binder;
         assertEquals("Lex Luthor", action.getName());
     }
 
+    public void testSimpleWrapped() throws ParseException, OgnlException {
+        String expr = "#{name}";
+        SimpleAction action = new SimpleAction();
+
+        assertNull(action.getName());
+
+        Map<String, Object> context = new HashMap<String, Object>();
+        binder.setProperty(context, action, expr, "Lex Luthor");
+
+        assertEquals("Lex Luthor", action.getName());
+    }
+
     public void testSimpleOnCompoundRoot() throws ParseException, OgnlException {
         String expr = "name";
         SimpleAction action = new SimpleAction();