- Revision
- 777
- Author
- mauro
- Date
- 2008-08-20 17:04:32 -0500 (Wed, 20 Aug 2008)
Log Message
WAFFLE-91: Added StringNumberListMapValueConverter
Modified Paths
- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/AbstractValueConverter.java
- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/NumberListValueConverter.java
- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/StringListValueConverter.java
- trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeControllerWithListMethods.java
- trunk/waffle-core/src/test/resources/FakeResourceBundle.properties
Added Paths
Diff
Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/AbstractValueConverter.java (776 => 777)
--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/AbstractValueConverter.java 2008-08-20 21:06:19 UTC (rev 776) +++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/AbstractValueConverter.java 2008-08-20 22:04:32 UTC (rev 777) @@ -3,15 +3,17 @@ */ package org.codehaus.waffle.bind.converters; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + import org.codehaus.waffle.bind.BindException; import org.codehaus.waffle.bind.ValueConverter; import org.codehaus.waffle.i18n.MessageResources; -import java.util.Properties; -import java.util.List; -import java.lang.reflect.Type; -import java.lang.reflect.ParameterizedType; - /** * Abstract <code>ValueConverter</code> that holds utility functionality common to all value converters. * @@ -53,7 +55,7 @@ /** * Accepts parameterized types of raw type List and argument of the type passed in */ - protected boolean accept(Type type, Class argumentClass) { + protected boolean acceptList(Type type, Class<?> argumentClass) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; Type rawType = parameterizedType.getRawType(); @@ -64,7 +66,22 @@ return false; } - @SuppressWarnings( { "ThrowableInstanceNeverThrown" }) + /** + * Accepts parameterized types of raw type Map and arguments of the type passed in + */ + protected boolean acceptMap(Type type, Class<?> argumentClass0, Class<?> argumentClass1) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Type rawType = parameterizedType.getRawType(); + Type argumentType0 = parameterizedType.getActualTypeArguments()[0]; + Type argumentType1 = parameterizedType.getActualTypeArguments()[1]; + return Map.class.isAssignableFrom((Class<?>) rawType) + && argumentClass0.isAssignableFrom((Class<?>) argumentType0) + && acceptList(argumentType1, argumentClass1); + } + return false; + } + protected BindException newBindException(String key, String defaultMessage, Object... parameters) { String message = messageResources.getMessageWithDefault(key, defaultMessage, parameters); return new BindException(message); @@ -92,4 +109,15 @@ public void changePatterns(Properties patterns) { this.patterns = patterns; } + + protected List<String> split(String value, String separator) { + String[] values = value.split(separator); + List<String> list = new ArrayList<String>(); + for (String current : values) { + if (current.trim().length() > 0) { + list.add(current.trim()); + } + } + return list; + } }
Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/NumberListValueConverter.java (776 => 777)
--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/NumberListValueConverter.java 2008-08-20 21:06:19 UTC (rev 776) +++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/NumberListValueConverter.java 2008-08-20 22:04:32 UTC (rev 777) @@ -39,7 +39,7 @@ * Accepts parameterized types of raw type List and argument type Number */ public boolean accept(Type type) { - return accept(type, Number.class); + return acceptList(type, Number.class); } @SuppressWarnings( { })
Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/StringListValueConverter.java (776 => 777)
--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/StringListValueConverter.java 2008-08-20 21:06:19 UTC (rev 776) +++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/StringListValueConverter.java 2008-08-20 22:04:32 UTC (rev 777) @@ -44,7 +44,7 @@ * Accepts parameterized types of raw type List and argument type String */ public boolean accept(Type type) { - return accept(type, String.class); + return acceptList(type, String.class); } @SuppressWarnings( { }) @@ -59,14 +59,7 @@ } protected List<String> listValues(String value) { - String[] values = value.split(COMMA); - List<String> list = new ArrayList<String>(); - for (String current : values) { - if (current.trim().length() > 0) { - list.add(current); - } - } - return list; + return split(value, COMMA); } protected Object convertMissingValue(String key, String defaultMessage, Object... parameters) {
Added: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/StringNumberListMapValueConverter.java (0 => 777)
--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/StringNumberListMapValueConverter.java (rev 0) +++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/StringNumberListMapValueConverter.java 2008-08-20 22:04:32 UTC (rev 777) @@ -0,0 +1,105 @@ +/* + * Copyright (c) terms as published in http://waffle.codehaus.org/license.html + */ +package org.codehaus.waffle.bind.converters; + +import java.lang.reflect.Type; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.codehaus.waffle.i18n.MessageResources; + +/** + * <p> + * <code>ValueConverter</code> that converts a text value to a Map of Number Lists indexed by Strings. It provides + * number parsing of the string values using the <code>NumberFormat</code> instance provided (which defaults to + * <code>NumberFormat.getInstance()</code>) and if not successful returns the string values themselves. + * </p> + * <p> + * A value of the form + * + * <pre> + * a=1\n + * b=1,2\n + * c=1,2,3 + * </pre> + * + * would be converted to a map + * + * <pre> + * Map<String, List<? extends Number>> map = new HashMap<String, List<? extends Number>>(); + * map.put("a", asList(1)); + * map.put("b", asList(1, 2)); + * map.put("c", asList(1, 2, 3)); + * </pre> + * + * </p> + * + * @author Mauro Talevi + */ +public class StringNumberListMapValueConverter extends AbstractValueConverter { + + public static final String BIND_ERROR_MAP_KEY = "bind.error.map"; + public static final String DEFAULT_MAP_MESSAGE = "Invalid map value for field {0}"; + private static final String NL = "\n"; + private static final String EQUAL = "="; + private static final String COMMA = ","; + + private NumberFormat numberFormat; + + public StringNumberListMapValueConverter(MessageResources messageResources) { + this(messageResources, new Properties(), NumberFormat.getInstance()); + } + + public StringNumberListMapValueConverter(MessageResources messageResources, Properties patterns, + NumberFormat numberFormat) { + super(messageResources, patterns); + this.numberFormat = numberFormat; + } + + /** + * Accepts parameterized types of raw type Map and argument types String and Number + */ + public boolean accept(Type type) { + return acceptMap(type, String.class, Number.class); + } + + public Object convertValue(String propertyName, String value, Type toType) { + + if (missingValue(value)) { + String fieldName = messageFor(propertyName, propertyName); + return convertMissingValue(BIND_ERROR_MAP_KEY, DEFAULT_MAP_MESSAGE, fieldName); + } + + List<String> lines = split(value, NL); + Map<String, List<Number>> map = new HashMap<String, List<Number>>(); + for (String line : lines) { + List<String> parts = split(line, EQUAL); + if (parts.size() > 1) { + try { + String csv = parts.get(1); + List<String> values = split(csv, COMMA); + List<Number> numbers = new ArrayList<Number>(); + for (String numberValue : values) { + numbers.add(numberFormat.parse(numberValue)); + } + map.put(parts.get(0), numbers); + } catch (ParseException e) { + // failed to parse as numbers, return string values + // TODO should we throw a bind exception here? + } + } + } + return map; + } + + protected Object convertMissingValue(String key, String defaultMessage, Object... parameters) { + return new ArrayList<Number>(); + } + +}
Added: trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/StringNumberListMapValueConverterTest.java (0 => 777)
--- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/StringNumberListMapValueConverterTest.java (rev 0) +++ trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/StringNumberListMapValueConverterTest.java 2008-08-20 22:04:32 UTC (rev 777) @@ -0,0 +1,109 @@ +package org.codehaus.waffle.bind.converters; + +import static java.text.MessageFormat.format; +import static java.util.Arrays.asList; +import static org.codehaus.waffle.testmodel.FakeControllerWithListMethods.methodParameterType; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.beans.IntrospectionException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import ognl.OgnlException; + +import org.codehaus.waffle.bind.BindException; +import org.codehaus.waffle.i18n.DefaultMessageResources; +import org.junit.Test; + +/** + * @author Mauro Talevi + */ +public class StringNumberListMapValueConverterTest extends AbstractValueConverterTest { + + @Test + public void canAccept() throws IntrospectionException { + StringNumberListMapValueConverter converter = new StringNumberListMapValueConverter( + new DefaultMessageResources()); + assertTrue(converter.accept(methodParameterType("mapOfStringIntegerLists"))); + assertFalse(converter.accept(List.class)); + assertFalse(converter.accept(Object.class)); + assertFalse(converter.accept(methodParameterType("object"))); + } + + @Test + public void canConvertMapsOfStringNumberLists() throws OgnlException { + StringNumberListMapValueConverter converter = new StringNumberListMapValueConverter( + new DefaultMessageResources()); + Map<String, List<? extends Number>> map = new HashMap<String, List<? extends Number>>(); + map.put("a", asList(1)); + map.put("b", asList(1, 2)); + map.put("c", asList(1, 2, 3)); + // Note: no conversion is done from String to Numbers and the assertion is done on the string representation + assertCanConvertValueToList(converter, map, "a=1\n b=1,2\n c=1,2,3"); + } + + @SuppressWarnings("unchecked") + private void assertCanConvertValueToList(StringNumberListMapValueConverter converter, + Map<String, List<? extends Number>> expected, String value) { + Map<String, List<? extends Number>> actual = (Map<String, List<? extends Number>>) converter.convertValue( + "property-name", value, Map.class); + assertEquals(expected.toString(), actual.toString()); + } + + @Test + public void canHandleMissingValues() { + StringNumberListMapValueConverter converter = new StringNumberListMapValueConverter( + new DefaultMessageResources()); + assertEmptyList(converter, null); + assertEmptyList(converter, ""); + assertEmptyList(converter, " "); + } + + private void assertEmptyList(StringNumberListMapValueConverter converter, String value) { + List<?> list = (List<?>) converter.convertValue("property-name", value, List.class); + assertNotNull(list); + assertTrue(list.isEmpty()); + } + + @Test + public void canFailConversionWithCustomErrorMessages() { + DefaultMessageResources resources = new DefaultMessageResources(configuration); + StringNumberListMapValueConverter converter = new StringNumberListMapValueConverter(resources) { + + @Override + protected Object convertMissingValue(String key, String defaultMessage, Object... parameters) { + throw newBindException(key, defaultMessage, parameters); + } + }; + try { + converter.convertValue("property-name", null, List.class); + fail("Expected BindException"); + } catch (BindException e) { + assertEquals(format(resources.getMessage(StringNumberListMapValueConverter.BIND_ERROR_MAP_KEY), + "property-name"), e.getMessage()); + } + } + + @Test + public void canFailConversionWithDefaultErrorMessages() { + StringNumberListMapValueConverter converter = new StringNumberListMapValueConverter( + new DefaultMessageResources()) { + @Override + protected Object convertMissingValue(String key, String defaultMessage, Object... parameters) { + throw newBindException(key, defaultMessage, parameters); + } + }; + try { + converter.convertValue("property-name", null, List.class); + fail("Expected BindException"); + } catch (BindException e) { + assertEquals(format(StringNumberListMapValueConverter.DEFAULT_MAP_MESSAGE, "property-name"), e.getMessage()); + } + } + +}
Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeControllerWithListMethods.java (776 => 777)
--- trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeControllerWithListMethods.java 2008-08-20 21:06:19 UTC (rev 776) +++ trunk/waffle-core/src/test/java/org/codehaus/waffle/testmodel/FakeControllerWithListMethods.java 2008-08-20 22:04:32 UTC (rev 777) @@ -6,6 +6,7 @@ import java.beans.MethodDescriptor; import java.lang.reflect.Type; import java.util.List; +import java.util.Map; public class FakeControllerWithListMethods { public static Type methodParameterType(String methodName) throws IntrospectionException { @@ -25,5 +26,6 @@ public void listOfLongs(List<Integer> list){}; public void listOfDoubles(List<Integer> list){}; public void listOfFloats(List<Integer> list){}; + public void mapOfStringIntegerLists(Map<String,List<Integer>> map){}; public void object(Object object){}; } \ No newline at end of file
Modified: trunk/waffle-core/src/test/resources/FakeResourceBundle.properties (776 => 777)
--- trunk/waffle-core/src/test/resources/FakeResourceBundle.properties 2008-08-20 21:06:19 UTC (rev 776) +++ trunk/waffle-core/src/test/resources/FakeResourceBundle.properties 2008-08-20 22:04:32 UTC (rev 777) @@ -8,3 +8,5 @@ bind.error.date=Date {1} has invalid format {2} for field {0} bind.error.date.missing=Date field {0} is missing bind.error.list=No list values for field {0} +bind.error.map=No map values for field {0} +
To unsubscribe from this list please visit:
