Title: [waffle-scm] [601] trunk/waffle-core/src/main/java/org/codehaus/waffle/bind: WAFFLE-65: Added DataBinder support for multiple-value parameters.

Diff

Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/DataBinder.java (600 => 601)

--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/DataBinder.java	2008-04-01 16:25:53 UTC (rev 600)
+++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/DataBinder.java	2008-04-02 14:08:08 UTC (rev 601)
@@ -16,12 +16,20 @@
 import javax.servlet.http.HttpServletResponse;
 
 /**
- * Implementor of this interface are responsible for binding the values from
- * the request to the model.
- *
+ * Implementor of this interface are responsible for binding the values from the request to the controller.
+ * 
  * @author Michael Ward
  */
 public interface DataBinder {
-    
-    void bind(HttpServletRequest request, HttpServletResponse response, ErrorsContext errorsContext, Object model);
+
+    /**
+     * Bind parameters values from the request to the controller
+     * 
+     * @param request the HttpServletRequest containing the parameter values
+     * @param response the HttpServletResponse
+     * @param errorsContext the ErrorsContext
+     * @param controller the controller instance
+     */
+    void bind(HttpServletRequest request, HttpServletResponse response, ErrorsContext errorsContext, Object controller);
+
 }

Added: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/ListValueConverter.java (0 => 601)

--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/ListValueConverter.java	                        (rev 0)
+++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/ListValueConverter.java	2008-04-02 14:08:08 UTC (rev 601)
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * Copyright (c) 2005-2008 Michael Ward                                      *
+ * All rights reserved.                                                      *
+ * ------------------------------------------------------------------------- *
+ * The software in this package is published under the terms of the BSD      *
+ * style license a copy of which has been included with this distribution in *
+ * the LICENSE.txt file.                                                     *
+ *                                                                           *
+ * Original code by: Mauro Talevi                                            *
+ *****************************************************************************/
+package org.codehaus.waffle.bind.converters;
+
+import static java.util.Arrays.asList;
+
+import java.util.List;
+
+import org.codehaus.waffle.bind.BindException;
+import org.codehaus.waffle.bind.ValueConverter;
+
+/**
+ * <code>ValueConverter</code> that converts a CSV value to a List of Strings.
+ * A <code>null</code> value will cause a BindException to thrown.
+ *
+ * @author Mauro Talevi
+ */
+public class ListValueConverter implements ValueConverter {
+
+    public boolean accept(Class<?> type) {
+        return List.class.isAssignableFrom(type);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    public <T> T convertValue(String propertyName, String value, Class<T> toType) {
+        if ( value == null ){
+            throw new BindException("Cannot convert null value for property "+propertyName);
+        }
+        return (T) asList(value.split(","));
+    }
+
+}

Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/ognl/DelegatingTypeConverter.java (600 => 601)

--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/ognl/DelegatingTypeConverter.java	2008-04-01 16:25:53 UTC (rev 600)
+++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/ognl/DelegatingTypeConverter.java	2008-04-02 14:08:08 UTC (rev 601)
@@ -54,6 +54,7 @@
      * @return Converted value Object of type toType or TypeConverter.NoConversionPossible to indicate that the
      *         conversion was not possible.
      */
+    @SuppressWarnings("unchecked")
     public Object convertValue(Map context,
                                Object target,
                                Member member,

Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/ognl/OgnlDataBinder.java (600 => 601)

--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/ognl/OgnlDataBinder.java	2008-04-01 16:25:53 UTC (rev 600)
+++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/ognl/OgnlDataBinder.java	2008-04-02 14:08:08 UTC (rev 601)
@@ -48,37 +48,60 @@
     }
     
     @SuppressWarnings({"unchecked"})
-    public void bind(HttpServletRequest request, HttpServletResponse response, ErrorsContext errorsContext, Object model) {
+    public void bind(HttpServletRequest request, HttpServletResponse response, ErrorsContext errorsContext, Object controller) {
         Enumeration<String> parameterNames = request.getParameterNames();
 
         while (parameterNames.hasMoreElements()) {
-            String name = parameterNames.nextElement();
-            String value = request.getParameter(name);
+            String name = parameterNames.nextElement();           
+            String value = getParameterValue(request, name);
 
             try {
-                handleConvert(name, value, model);
+                handleConvert(name, value, controller);
             } catch (OgnlException e) {
-                String message = bindErrorMessageResolver.resolve(model, name, value);
+                String message = bindErrorMessageResolver.resolve(controller, name, value);
                 BindErrorMessage errorMessage = new BindErrorMessage(name, value, message);
                 errorsContext.addErrorMessage(errorMessage);
-                bindMonitor.bindFailedForModel(model, errorMessage);                
+                bindMonitor.bindFailedForModel(controller, errorMessage);                
             } catch (BindException e) {
                 // by convention BindExceptions should provide the correct bind error message to display to the end-user
                 BindErrorMessage errorMessage = new BindErrorMessage(name, value, e.getMessage());
                 errorsContext.addErrorMessage(errorMessage);
-                bindMonitor.bindFailedForModel(model, errorMessage);
+                bindMonitor.bindFailedForModel(controller, errorMessage);
             }
         }
     }
 
+    private String getParameterValue(HttpServletRequest request, String name) {
+        // Look for multiple values and join them if found
+        String[] values = request.getParameterValues(name);
+        if ( values == null ){
+            return null;
+        }
+        // Joining a single value will return the equivalent of request.getParameter(name)
+        return join(values, ",");
+    }
+
+    // Could use commons-lang StringUtils.join() but avoid introducing a dependency for such a trivial operation
+    private String join(String[] values, String separator) {
+        StringBuilder sb = new StringBuilder();
+        for ( int i = 0; i < values.length; i++ ){
+            sb.append(values[i]);
+            if ( i < values.length - 1 ){
+                sb.append(separator);
+            }
+        }
+        return sb.toString();
+    }
+
+    @SuppressWarnings("unchecked")
     protected void handleConvert(String propertyName,
                                  String parameterValue,
-                                 Object model) throws OgnlException, BindException {
+                                 Object controller) throws OgnlException, BindException {
         try {
             Object tree = Ognl.parseExpression(propertyName);
-            Map ognlContext = Ognl.createDefaultContext(model);
+            Map ognlContext = Ognl.createDefaultContext(controller);
             Ognl.setTypeConverter(ognlContext, typeConverter);
-            Ognl.setValue(tree, ognlContext, model, parameterValue);
+            Ognl.setValue(tree, ognlContext, controller, parameterValue);
         } catch (NoSuchPropertyException ignore) {
             // ignore NoSuchPropertyException
         } catch (InappropriateExpressionException ignore) {

Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/DelegatingTypeConverterTest.java (600 => 601)

--- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/DelegatingTypeConverterTest.java	2008-04-01 16:25:53 UTC (rev 600)
+++ trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/DelegatingTypeConverterTest.java	2008-04-02 14:08:08 UTC (rev 601)
@@ -1,10 +1,14 @@
 package org.codehaus.waffle.bind.ognl;
 
+import static java.util.Arrays.asList;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
 
-import java.util.Vector;
+import java.util.List;
 
+import org.codehaus.waffle.bind.BindException;
 import org.codehaus.waffle.bind.ValueConverter;
+import org.codehaus.waffle.bind.converters.ListValueConverter;
 import org.codehaus.waffle.context.ContextLevel;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
@@ -37,21 +41,43 @@
 
         assertEquals(15, value);
     }
+    
+    @Test
+    public void canDelegateToListValueConverter() {
+        final ValueConverter valueConverter = new ListValueConverter();
+        final List<String> list = asList("one","two");
+        DelegatingTypeConverter converter = new DelegatingTypeConverter(new OgnlValueConverterFinder(valueConverter));
 
+        Object convertedValue = converter.convertValue("propertyName", "one,two", List.class);
+        assertEquals(convertedValue, list);
+    }
+    
+    @Test(expected=BindException.class)
+    public void cannotDelegateToListValueConverterNullValue() {
+        final ValueConverter valueConverter = new ListValueConverter();
+        DelegatingTypeConverter converter = new DelegatingTypeConverter(new OgnlValueConverterFinder(valueConverter));
+        
+        converter.convertValue("propertyName", null, List.class);
+    }
+    
     @Test
-    public void canDelegateToValueConverter() {
+    public void canDelegateToCustomValueConverter() {
         // Mock ValueConverter 
         final ValueConverter valueConverter = mockery.mock(ValueConverter.class);
+        final CustomType type = new CustomType(){};
         mockery.checking(new Expectations() {
             {
-                one(valueConverter).accept(Vector.class);
+                one(valueConverter).accept(CustomType.class);
                 will(returnValue(true));
-                one(valueConverter).convertValue(with(same("propertyName")), with(same("foobar")), with(same(Vector.class)));
-                will(returnValue(new Vector<Object>()));
+                one(valueConverter).convertValue(with(same("propertyName")), with(same("foobar")), with(same(CustomType.class)));
+                will(returnValue(type));
             }
         });
         DelegatingTypeConverter converter = new DelegatingTypeConverter(new OgnlValueConverterFinder(valueConverter));
 
-        converter.convertValue("propertyName", "foobar", Vector.class);
+        Object convertedValue = converter.convertValue("propertyName", "foobar", CustomType.class);
+        assertSame(convertedValue, type);
     }
+    
+    private static interface CustomType {};
 }

Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/OgnlDataBinderTest.java (600 => 601)

--- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/OgnlDataBinderTest.java	2008-04-01 16:25:53 UTC (rev 600)
+++ trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/OgnlDataBinderTest.java	2008-04-02 14:08:08 UTC (rev 601)
@@ -1,11 +1,13 @@
 package org.codehaus.waffle.bind.ognl;
 
+import static java.util.Arrays.asList;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.List;
@@ -15,6 +17,7 @@
 import org.codehaus.waffle.bind.BindErrorMessageResolver;
 import org.codehaus.waffle.bind.BindException;
 import org.codehaus.waffle.bind.DataBinder;
+import org.codehaus.waffle.bind.converters.ListValueConverter;
 import org.codehaus.waffle.context.ContextLevel;
 import org.codehaus.waffle.monitor.SilentMonitor;
 import org.codehaus.waffle.testmodel.FakeBean;
@@ -40,7 +43,7 @@
     private Mockery mockery = new Mockery();
 
     @Test
-    public void canBind() {
+    public void canBindSingleValue() {
         List<String> parameters = new ArrayList<String>();
         parameters.add("name");
         parameters.add("contextLevel");
@@ -52,10 +55,10 @@
             {
                 one(request).getParameterNames();
                 will(returnValue(enumeration));
-                one(request).getParameter("name");
-                will(returnValue("foobar"));
-                one(request).getParameter("contextLevel");
-                will(returnValue("APPLICATION"));
+                one(request).getParameterValues("name");
+                will(returnValue(new String[]{"foobar"}));
+                one(request).getParameterValues("contextLevel");
+                will(returnValue(new String[]{"APPLICATION"}));
             }
         });
 
@@ -68,7 +71,34 @@
         assertEquals(ContextLevel.APPLICATION, fakeController.getContextLevel());
         assertFalse(errorsContext.hasErrorMessages());
     }
+    
+    @Test
+    public void canBindListValues() {
+        List<String> parameters = new ArrayList<String>();
+        parameters.add("list");
+        final Enumeration<String> enumeration = Collections.enumeration(parameters);
 
+        // Mock HttpServletRequest
+        final String[] values = new String[]{"foo", "bar"};
+        final HttpServletRequest request = mockery.mock(HttpServletRequest.class);
+        mockery.checking(new Expectations() {
+            {
+                one(request).getParameterNames();
+                will(returnValue(enumeration));
+                one(request).getParameterValues("list");
+                will(returnValue(values));
+            }
+        });
+
+        FakeController fakeController = new FakeController();
+        DataBinder binder = new OgnlDataBinder(new OgnlValueConverterFinder(new ListValueConverter()), null, new SilentMonitor());
+        ErrorsContext errorsContext = new DefaultErrorsContext(null);
+        binder.bind(request, null, errorsContext, fakeController);
+
+        assertEquals(asList(values), fakeController.getList());
+        assertFalse(errorsContext.hasErrorMessages());
+    }
+       
     @Test
     public void canBindEmptyValueForEnum() {
         List<String> parameters = new ArrayList<String>();
@@ -81,8 +111,8 @@
             {
                 one(request).getParameterNames();
                 will(returnValue(enumeration));
-                one(request).getParameter("contextLevel");
-                will(returnValue(""));
+                one(request).getParameterValues("contextLevel");
+                will(returnValue(new String[]{""}));
             }
         });
 
@@ -107,8 +137,8 @@
             {
                 one(request).getParameterNames();
                 will(returnValue(enumeration));
-                one(request).getParameter("method");
-                will(returnValue("this should cause a NoSuchPropertyException!"));
+                one(request).getParameterValues("method");
+                will(returnValue(new String[]{"this should cause a NoSuchPropertyException!"}));
             }
         });
 
@@ -136,8 +166,8 @@
             {
                 one(request).getParameterNames();
                 will(returnValue(enumeration));
-                one(request).getParameter("x-01234567-s");
-                will(returnValue("blah"));
+                one(request).getParameterValues("x-01234567-s");
+                will(returnValue(new String[]{"blah"}));
             }
         });
 
@@ -161,8 +191,8 @@
             {
                 one(request).getParameterNames();
                 will(returnValue(enumeration));
-                one(request).getParameter("count");
-                will(returnValue("bad value"));
+                one(request).getParameterValues("count");
+                will(returnValue(new String[]{"bad value"}));
             }
         });
 
@@ -170,7 +200,7 @@
         final BindErrorMessageResolver resolver = mockery.mock(BindErrorMessageResolver.class);
         mockery.checking(new Expectations() {
             {
-                one(resolver).resolve(with(an(FakeBean.class)), with(same("count")), with(same("bad value")));
+                one(resolver).resolve(with(an(FakeBean.class)), with(equal("count")), with(equal("bad value")));
             }
         });
 
@@ -200,8 +230,8 @@
             {
                 one(request).getParameterNames();
                 will(returnValue(enumeration));
-                one(request).getParameter("count");
-                will(returnValue("bad value"));
+                one(request).getParameterValues("count");
+                will(returnValue(new String[]{"bad value"}));
             }
         });
 

Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeController.java (600 => 601)

--- trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeController.java	2008-04-01 16:25:53 UTC (rev 600)
+++ trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeController.java	2008-04-02 14:08:08 UTC (rev 601)
@@ -10,18 +10,21 @@
  *****************************************************************************/
 package org.codehaus.waffle.testmodel;
 
-import org.codehaus.waffle.context.ContextLevel;
-import org.codehaus.waffle.view.View;
-import org.codehaus.waffle.action.ActionMethodInvocationException;
-import org.codehaus.waffle.action.ActionMethodException;
+import java.util.List;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpSession;
 
+import org.codehaus.waffle.action.ActionMethodException;
+import org.codehaus.waffle.action.ActionMethodInvocationException;
+import org.codehaus.waffle.context.ContextLevel;
+import org.codehaus.waffle.view.View;
+
 public class FakeController {
     private String name;
     private String[] values;
+    private List<String> list;
     private Long numericValue;
     private ContextLevel contextLevel;
     private HttpServletRequest request;
@@ -43,6 +46,7 @@
     public void setNumericValue(Long numericValue) {
         this.numericValue = numericValue;
     }// used to prove we can set an enum with ognl
+
     public ContextLevel getContextLevel() {
         return contextLevel;
     }
@@ -83,6 +87,14 @@
         return values;
     }
 
+    public List<String> getList() {
+        return list;
+    }
+
+    public void setList(List<String> list) {
+        this.list = list;
+    }
+
     public void sayHelloAlso(StringBuffer sb) {
         setName(sb.toString());
     }
@@ -117,6 +129,6 @@
     }
 
     public String toString() {
-        return "FakeController: " + name + " hash: " + hashCode();
+        return "[FakeController name: " + name + ", list: "+list+", hash: " + hashCode()+"]";
     }
 }

Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/StubMonitor.java (600 => 601)

--- trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/StubMonitor.java	2008-04-01 16:25:53 UTC (rev 600)
+++ trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/StubMonitor.java	2008-04-02 14:08:08 UTC (rev 601)
@@ -1,8 +1,14 @@
 package org.codehaus.waffle.testmodel;
 
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletResponse;
+
 import org.codehaus.waffle.action.ActionMethodResponse;
+import org.codehaus.waffle.action.MethodDefinition;
 import org.codehaus.waffle.action.HierarchicalArgumentResolver.Scope;
-import org.codehaus.waffle.action.MethodDefinition;
 import org.codehaus.waffle.context.ContextContainer;
 import org.codehaus.waffle.controller.ControllerDefinition;
 import org.codehaus.waffle.monitor.ActionMonitor;
@@ -19,11 +25,6 @@
 import org.codehaus.waffle.view.ResponderView;
 import org.codehaus.waffle.view.View;
 
-import javax.servlet.http.HttpServletResponse;
-import java.lang.reflect.Method;
-import java.util.Map;
-import java.util.Set;
-
 public class StubMonitor implements ActionMonitor, BindMonitor, ContextMonitor, ControllerMonitor, RegistrarMonitor,
         ServletMonitor, ValidationMonitor, ViewMonitor {
 
@@ -69,6 +70,9 @@
     public void bindFailedForController(Object controller, Throwable cause) {
     }
 
+    public void valueBound(String name, String value, Object controller) {
+    }
+
     public void contextInitialized() {
     }
 


To unsubscribe from this list please visit:

http://xircles.codehaus.org/manage_email

Reply via email to