Title: [waffle-scm] [777] trunk/waffle-core/src/test/resources: WAFFLE-91: Added StringNumberListMapValueConverter

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&lt;String, List&lt;? extends Number&gt;&gt; map = new HashMap&lt;String, List&lt;? extends Number&gt;&gt;();
+ * map.put(&quot;a&quot;, asList(1));
+ * map.put(&quot;b&quot;, asList(1, 2));
+ * map.put(&quot;c&quot;, 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:

http://xircles.codehaus.org/manage_email

Reply via email to