Title: [waffle-scm] [727] trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters: WAFFLE-85: Addd NumberListValueConverter.

Diff

Modified: trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/ListValueConverter.java (726 => 727)

--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/ListValueConverter.java	2008-06-17 15:31:18 UTC (rev 726)
+++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/ListValueConverter.java	2008-06-17 16:23:43 UTC (rev 727)
@@ -5,8 +5,6 @@
 
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
-import java.text.NumberFormat;
-import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
@@ -16,24 +14,15 @@
 /**
  * <p>
  * <code>ValueConverter</code> that converts a CSV value to a List. A <code>null</code> or empty value (once
- * trimmed) will be returned as an empty list (behaviour which can be overridden via the
- * [EMAIL PROTECTED] #convertMissingValue} method). The message keys and default values used are:
+ * trimmed) will be returned as an empty list (behaviour which can be overridden via the [EMAIL PROTECTED] #convertMissingValue}
+ * method). The message keys and default values used are:
  * <ul>
  * <li>"bind.error.list" ([EMAIL PROTECTED] #BIND_ERROR_LIST_KEY}): list is <code>null</code> or empty (message defaults to
  * [EMAIL PROTECTED] #DEFAULT_LIST_MESSAGE})</li>
- * <li>"list.number.pattern" ([EMAIL PROTECTED] #NUMBER_PATTERN_KEY}): pattern to use to identify the list as parseable numbers
- * (defaults to [EMAIL PROTECTED] #DEFAULT_NUMBER_PATTERN})</li>
  * </ul>
  * The patterns are also optionally injectable via <code>Properties</code> in the constructor and take precedence over
  * the ones configured in the messages resources.
  * </p>
- * <p>
- * NOTE: the converter will first check if the values match the configured number regex pattern and only if it does will
- * it attempt to parse them (using the <code>NumberFormat</code> instance provided, which defaults to
- * <code>NumberFormat.getInstance()</code>) and if not successful returns the string values. The reason for the
- * presence of the preliminary number pattern matching is to disable the attempt of number parsing altogether for some
- * string values that may start with number and may be erronously parsed as numbers.
- * </p>
  * 
  * @author Mauro Talevi
  */
@@ -41,29 +30,25 @@
 
     public static final String BIND_ERROR_LIST_KEY = "bind.error.list";
     public static final String DEFAULT_LIST_MESSAGE = "Invalid list value for field {0}";
-    public static final String NUMBER_PATTERN_KEY = "list.number.pattern";
-    public static final String DEFAULT_NUMBER_PATTERN = "[0-9.-]*";
-    
+
     private static final String COMMA = ",";
-    private NumberFormat numberFormat;
     private Properties patterns;
 
     public ListValueConverter(MessageResources messageResources) {
-        this(messageResources, NumberFormat.getInstance(), new Properties());
+        this(messageResources, new Properties());
     }
 
-    public ListValueConverter(MessageResources messageResources, NumberFormat numberFormat, Properties patterns) {
+    public ListValueConverter(MessageResources messageResources, Properties patterns) {
         super(messageResources);
-        this.numberFormat = numberFormat;
         this.patterns = patterns;
     }
 
     public boolean accept(Type type) {
-        if ( type instanceof Class ){
-            return List.class.isAssignableFrom((Class<?>)type);            
-        } else if ( type instanceof ParameterizedType ){            
-            Type rawType = ((ParameterizedType)type).getRawType();
-            return List.class.isAssignableFrom((Class<?>)rawType);
+        if (type instanceof Class) {
+            return List.class.isAssignableFrom((Class<?>) type);
+        } else if (type instanceof ParameterizedType) {
+            Type rawType = ((ParameterizedType) type).getRawType();
+            return List.class.isAssignableFrom((Class<?>) rawType);
         }
         return false;
     }
@@ -76,22 +61,14 @@
             return convertMissingValue(BIND_ERROR_LIST_KEY, DEFAULT_LIST_MESSAGE, fieldName);
         }
 
-        List<String> values = listValues(value);
-        if (areNumbers(values)) {
-            try {
-                return toNumbers(values);
-            } catch (ParseException e) {
-                // failed to parse as numbers, return string values
-            }
-        }
-        return values;
+        return listValues(value);
     }
 
     private List<String> listValues(String value) {
-        String[] values = value.split(COMMA);        
+        String[] values = value.split(COMMA);
         List<String> list = new ArrayList<String>();
-        for ( String current : values ){
-            if ( current.trim().length() > 0 ){
+        for (String current : values) {
+            if (current.trim().length() > 0) {
                 list.add(current);
             }
         }
@@ -110,26 +87,5 @@
     protected Object convertMissingValue(String key, String defaultMessage, Object... parameters) {
         return new ArrayList();
     }
-    
-    protected boolean areNumbers(List<String> values) {
-        if (values.size() == 0) {
-            return false; // return empty list
-        }
-        String numberPattern = patternFor(patterns, NUMBER_PATTERN_KEY, DEFAULT_NUMBER_PATTERN);
-        for (String value : values) {
-            if (!matches(value, numberPattern)) {
-                return false;
-            }
-        }
-        return true;
-    }
-    
-    protected List<Number> toNumbers(List<String> values) throws ParseException {
-        List<Number> numbers = new ArrayList<Number>();
-        for (String value : values) {
-            numbers.add(numberFormat.parse(value));
-        }
-        return numbers;
-    }
 
 }

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

--- trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/NumberListValueConverter.java	                        (rev 0)
+++ trunk/waffle-core/src/main/java/org/codehaus/waffle/bind/converters/NumberListValueConverter.java	2008-06-17 16:23:43 UTC (rev 727)
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) terms as published in http://waffle.codehaus.org/license.html
+ */
+package org.codehaus.waffle.bind.converters;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.codehaus.waffle.i18n.MessageResources;
+
+/**
+ * <p>
+ * <code>ValueConverter</code> that converts a CSV value to a List. A <code>null</code> or empty value (once
+ * trimmed) will be returned as an empty list (behaviour which can be overridden via the [EMAIL PROTECTED] #convertMissingValue}
+ * method). The message keys and default values used are:
+ * <ul>
+ * <li>"bind.error.list" ([EMAIL PROTECTED] #BIND_ERROR_LIST_KEY}): list is <code>null</code> or empty (message defaults to
+ * [EMAIL PROTECTED] #DEFAULT_LIST_MESSAGE})</li>
+ * </ul>
+ * The patterns are also optionally injectable via <code>Properties</code> in the constructor and take precedence over
+ * the ones configured in the messages resources.
+ * </p>
+ * <p>
+ * NOTE: the converter will first check if the values match the configured number regex pattern and only if it does will
+ * it attempt to parse them (using the <code>NumberFormat</code> instance provided, which defaults to
+ * <code>NumberFormat.getInstance()</code>) and if not successful returns the string values. The reason for the
+ * presence of the preliminary number pattern matching is to disable the attempt of number parsing altogether for some
+ * string values that may start with number and may be erronously parsed as numbers.
+ * </p>
+ * 
+ * @author Mauro Talevi
+ */
+public class NumberListValueConverter extends AbstractValueConverter {
+
+    public static final String BIND_ERROR_LIST_KEY = "bind.error.list";
+    public static final String DEFAULT_LIST_MESSAGE = "Invalid list value for field {0}";
+
+    private static final String COMMA = ",";
+    private NumberFormat numberFormat;
+    private Properties patterns;
+
+    public NumberListValueConverter(MessageResources messageResources) {
+        this(messageResources, NumberFormat.getInstance(), new Properties());
+    }
+
+    public NumberListValueConverter(MessageResources messageResources, NumberFormat numberFormat, Properties patterns) {
+        super(messageResources);
+        this.numberFormat = numberFormat;
+        this.patterns = patterns;
+    }
+
+    /**
+     * Accepts types of raw type List and argument type Number
+     */
+    public boolean accept(Type type) {
+        if (type instanceof Class) {
+            return List.class.isAssignableFrom((Class<?>) type);
+        } else if (type instanceof ParameterizedType) {
+            ParameterizedType parameterizedType = (ParameterizedType) type;
+            Type rawType = parameterizedType.getRawType();
+            Type argumentType = parameterizedType.getActualTypeArguments()[0];
+            return List.class.isAssignableFrom((Class<?>) rawType) && Number.class.isAssignableFrom((Class<?>)argumentType);
+        }
+        return false;
+    }
+
+    @SuppressWarnings( { "unchecked" })
+    public Object convertValue(String propertyName, String value, Type toType) {
+
+        if (missingValue(value)) {
+            String fieldName = messageFor(propertyName, propertyName);
+            return convertMissingValue(BIND_ERROR_LIST_KEY, DEFAULT_LIST_MESSAGE, fieldName);
+        }
+
+        List<String> values = listValues(value);
+        try {
+            return toNumbers(values);
+        } catch (ParseException e) {
+            // failed to parse as numbers, return string values
+        }
+        return values;
+    }
+
+    private 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;
+    }
+
+    public Properties getPatterns() {
+        return patterns;
+    }
+
+    public void changePatterns(Properties patterns) {
+        this.patterns = patterns;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected Object convertMissingValue(String key, String defaultMessage, Object... parameters) {
+        return new ArrayList();
+    }
+
+    protected List<Number> toNumbers(List<String> values) throws ParseException {
+        List<Number> numbers = new ArrayList<Number>();
+        for (String value : values) {
+            numbers.add(numberFormat.parse(value));
+        }
+        return numbers;
+    }
+
+}

Modified: trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/ListValueConverterTest.java (726 => 727)

--- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/ListValueConverterTest.java	2008-06-17 15:31:18 UTC (rev 726)
+++ trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/ListValueConverterTest.java	2008-06-17 16:23:43 UTC (rev 727)
@@ -48,22 +48,24 @@
     }
 
     @Test
-    public void canConvert() throws OgnlException {
+    public void canConvertLists() throws OgnlException {
         DefaultMessageResources resources = new DefaultMessageResources(configuration);
         ListValueConverter converter = new ListValueConverter(resources);
-        assertCanConvertValueToList(converter, INTEGERS, "-1,-2,-3", Integer.class);
-        assertCanConvertValueToList(converter, LONGS, "1000,2000,3000", Long.class);
-        assertCanConvertValueToList(converter, DOUBLES, "0.1,0.2,0.3", Double.class);
-        assertCanConvertValueToList(converter, FLOATS, "0.1,0.2,0.3", Float.class);
-        assertCanConvertValueToList(converter, STRINGS, "one,two,three", String.class);
-        assertCanConvertValueToList(converter, STRINGS, ",one,two,three", String.class);
-        assertCanConvertValueToList(converter, STRINGS, "one,,two,three", String.class);
-        assertCanConvertValueToList(converter, MIXED_STRINGS, "0#.A,1#.B", String.class);
+        //Note:  no conversion is done from String to Numbers and the assertion is done on the string representation
+        assertCanConvertValueToList(converter, INTEGERS, "-1,-2,-3");
+        assertCanConvertValueToList(converter, LONGS, "1000,2000,3000");
+        assertCanConvertValueToList(converter, DOUBLES, "0.1,0.2,0.3");
+        assertCanConvertValueToList(converter, FLOATS, "0.1,0.2,0.3");
+        assertCanConvertValueToList(converter, STRINGS, "one,two,three");
+        assertCanConvertValueToList(converter, STRINGS, ",one,two,three");
+        assertCanConvertValueToList(converter, STRINGS, "one,,two,three");
+        assertCanConvertValueToList(converter, MIXED_STRINGS, "0#.A,1#.B");
     }
 
-    private void assertCanConvertValueToList(ListValueConverter converter, List<?> list, String value, Class<?> type) {
-        assertEquals(list.toString(), converter.convertValue("property-name", value, List.class).toString());
-        assertTrue(list.get(0).getClass().isAssignableFrom(type));
+    @SuppressWarnings("unchecked")
+    private void assertCanConvertValueToList(ListValueConverter converter, List<?> expected, String value) {
+        List<String> actual = (List<String>) converter.convertValue("property-name", value, List.class);
+        assertEquals(expected.toString(), actual.toString());
     }
 
     @Test

Added: trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/NumberListValueConverterTest.java (0 => 727)

--- trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/NumberListValueConverterTest.java	                        (rev 0)
+++ trunk/waffle-core/src/test/java/org/codehaus/waffle/bind/converters/NumberListValueConverterTest.java	2008-06-17 16:23:43 UTC (rev 727)
@@ -0,0 +1,151 @@
+package org.codehaus.waffle.bind.converters;
+
+import static java.text.MessageFormat.format;
+import static java.util.Arrays.asList;
+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.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.MethodDescriptor;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Locale;
+
+import ognl.OgnlException;
+
+import org.codehaus.waffle.bind.BindException;
+import org.codehaus.waffle.i18n.DefaultMessageResources;
+import org.codehaus.waffle.i18n.MessageResourcesConfiguration;
+import org.junit.Test;
+
+/**
+ * 
+ * @author Mauro Talevi
+ */
+public class NumberListValueConverterTest {
+
+    private static final List<Integer> INTEGERS = asList(-1,-2,-3);
+    private static final List<Long> LONGS = asList(1000L,2000L,3000L);
+    private static final List<Double> DOUBLES = asList(0.1d,0.2d,0.3d);
+    private static final List<Float> FLOATS = asList(0.1f,0.2f,0.3f);
+    private static final List<String> STRINGS = asList("one","two","three");
+
+    private MessageResourcesConfiguration configuration = new MessageResourcesConfiguration(){
+
+        public Locale getDefaultLocale() {
+            return Locale.UK;
+        }
+
+        public String getResourceBundleName() {
+            return "FakeResourceBundle";
+        }
+        
+    };
+
+    private Type listType(String methodName) throws IntrospectionException {
+        BeanInfo beanInfo = Introspector.getBeanInfo(CustomType.class);
+        for (MethodDescriptor md : beanInfo.getMethodDescriptors()) {
+            if (md.getMethod().getName().equals(methodName)) {
+                return md.getMethod().getGenericParameterTypes()[0];
+            }
+        }
+        return null;
+    }
+
+    private static interface CustomType {       
+        void listOfStrings(List<String> list);
+        void listOfIntegers(List<Integer> list);
+        void listOfLongs(List<Integer> list);
+        void listOfDoubles(List<Integer> list);
+        void listOfFloats(List<Integer> list);
+    };
+    
+    @Test
+    public void canAccept() throws IntrospectionException {
+        NumberListValueConverter converter = new NumberListValueConverter(new DefaultMessageResources());
+        assertTrue(converter.accept(listType("listOfIntegers")));
+        assertTrue(converter.accept(listType("listOfLongs")));
+        assertTrue(converter.accept(listType("listOfDoubles")));
+        assertTrue(converter.accept(listType("listOfFloats")));
+        assertFalse(converter.accept(listType("listOfStrings")));
+    }
+
+    @Test
+    public void canConvertListsOfNumbers() throws OgnlException, IntrospectionException {
+        DefaultMessageResources resources = new DefaultMessageResources(configuration);
+        NumberListValueConverter converter = new NumberListValueConverter(resources);
+        assertCanConvertValueToList(converter, INTEGERS, "-1,-2,-3", Long.class, "listOfIntegers");
+        assertCanConvertValueToList(converter, LONGS, "1000,2000,3000", Long.class, "listOfLongs");
+        assertCanConvertValueToList(converter, DOUBLES, "0.1,0.2,0.3", Double.class, "listOfDoubles");
+        assertCanConvertValueToList(converter, FLOATS, "0.1,0.2,0.3", Double.class, "listOfFloats");
+    }
+
+    @Test //TODO decide if this behaviour is appropriate or if a bind exception should be thrown
+    public void canReturnListOfStringsIfParsingFails() throws OgnlException, IntrospectionException {
+        DefaultMessageResources resources = new DefaultMessageResources(configuration);
+        NumberListValueConverter converter = new NumberListValueConverter(resources);
+        assertCanConvertValueToList(converter, STRINGS, "one,two,three", String.class, "listOfStrings");
+    }
+
+    @SuppressWarnings("unchecked")
+    private void assertCanConvertValueToList(NumberListValueConverter converter, List<?> expected, String value, Class<?> expectedType, String methodName) throws IntrospectionException {
+        List<?> actual = (List<?>) converter.convertValue("property-name", value, listType(methodName));
+        assertEquals(expected.toString(), actual.toString());
+        assertTrue(expectedType.isAssignableFrom(actual.get(0).getClass()));
+    }
+
+    @Test
+    public void canHandleMissingValues() {
+        NumberListValueConverter converter = new NumberListValueConverter(new DefaultMessageResources());
+        assertEmptyList(converter, null);
+        assertEmptyList(converter, "");
+        assertEmptyList(converter, " ");
+     }
+
+    private void assertEmptyList(NumberListValueConverter 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);
+        NumberListValueConverter converter = new NumberListValueConverter(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(NumberListValueConverter.BIND_ERROR_LIST_KEY), "property-name"), e.getMessage());
+        }
+     }
+
+    @Test
+    public void canFailConversionWithDefaultErrorMessages() {
+        NumberListValueConverter converter = new NumberListValueConverter(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(NumberListValueConverter.DEFAULT_LIST_MESSAGE, "property-name"), e.getMessage());
+        }
+     }
+
+}
+


To unsubscribe from this list please visit:

http://xircles.codehaus.org/manage_email

Reply via email to