- Revision
- 601
- Author
- mauro
- Date
- 2008-04-02 09:08:08 -0500 (Wed, 02 Apr 2008)
Log Message
WAFFLE-65: Added DataBinder support for multiple-value parameters. OgnlDataBinder encodes multiple-values as CSV. Added ListValueConverter to handle CSV as List of Strings.
Modified Paths
- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/DataBinder.java
- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/ognl/DelegatingTypeConverter.java
- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/ognl/OgnlDataBinder.java
- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/DelegatingTypeConverterTest.java
- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/ognl/OgnlDataBinderTest.java
- trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeController.java
- trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/StubMonitor.java
Added Paths
- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/
- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/ListValueConverter.java
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:
