You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@batchee.apache.org by rm...@apache.org on 2015/11/24 12:08:23 UTC

incubator-batchee git commit: better coercing for commons csv mapper

Repository: incubator-batchee
Updated Branches:
  refs/heads/master 7dc84fcb6 -> 6047c395c


better coercing for commons csv mapper


Project: http://git-wip-us.apache.org/repos/asf/incubator-batchee/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-batchee/commit/6047c395
Tree: http://git-wip-us.apache.org/repos/asf/incubator-batchee/tree/6047c395
Diff: http://git-wip-us.apache.org/repos/asf/incubator-batchee/diff/6047c395

Branch: refs/heads/master
Commit: 6047c395cd4bcca76bbefa6a44c5ca94135bb066
Parents: 7dc84fc
Author: Romain Manni-Bucau <rm...@gmail.com>
Authored: Tue Nov 24 12:08:20 2015 +0100
Committer: Romain Manni-Bucau <rm...@gmail.com>
Committed: Tue Nov 24 12:08:20 2015 +0100

----------------------------------------------------------------------
 extensions/commons-csv/pom.xml                  |   7 +
 .../batchee/csv/mapper/CoercingConverter.java   |  21 +++
 .../batchee/csv/mapper/DefaultMapper.java       |  25 +++-
 .../apache/batchee/csv/mapper/Primitives.java   | 146 ++++++++++++-------
 .../batchee/csv/mapper/XBeanConverter.java      |  37 +++++
 .../batchee/csv/mapper/PrimitivesTest.java      |  54 +++++++
 6 files changed, 236 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/6047c395/extensions/commons-csv/pom.xml
----------------------------------------------------------------------
diff --git a/extensions/commons-csv/pom.xml b/extensions/commons-csv/pom.xml
index c0096fc..96fb793 100644
--- a/extensions/commons-csv/pom.xml
+++ b/extensions/commons-csv/pom.xml
@@ -42,5 +42,12 @@
       <artifactId>commons-csv</artifactId>
       <version>1.2</version>
     </dependency>
+
+    <dependency>
+      <groupId>org.apache.xbean</groupId>
+      <artifactId>xbean-reflect</artifactId>
+      <version>4.4</version>
+      <optional>true</optional>
+    </dependency>
   </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/6047c395/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/CoercingConverter.java
----------------------------------------------------------------------
diff --git a/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/CoercingConverter.java b/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/CoercingConverter.java
new file mode 100644
index 0000000..66d5a86
--- /dev/null
+++ b/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/CoercingConverter.java
@@ -0,0 +1,21 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.batchee.csv.mapper;
+
+public interface CoercingConverter {
+    Object valueFor(Class<?> type, String value);
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/6047c395/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/DefaultMapper.java
----------------------------------------------------------------------
diff --git a/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/DefaultMapper.java b/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/DefaultMapper.java
index 93e59b7..b7de7d1 100644
--- a/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/DefaultMapper.java
+++ b/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/DefaultMapper.java
@@ -29,6 +29,7 @@ import java.util.TreeMap;
 
 public class DefaultMapper<T> implements CsvReaderMapper<T>, CsvWriterMapper<T> {
     private final Class<T> type;
+    private final CoercingConverter coercingConverter;
 
     private final SortedMap<Integer, Field> fieldByPosition = new TreeMap<Integer, Field>();
     private final SortedMap<String, Field> fieldByName = new TreeMap<String, Field>();
@@ -36,7 +37,12 @@ public class DefaultMapper<T> implements CsvReaderMapper<T>, CsvWriterMapper<T>
     private final int maxIndex;
 
     public DefaultMapper(final Class<T> type) {
+        this(type, loadConverter());
+    }
+
+    protected DefaultMapper(final Class<T> type, final CoercingConverter coercingConverter) {
         this.type = type;
+        this.coercingConverter = coercingConverter;
 
         int higherIdx = -1;
 
@@ -137,10 +143,23 @@ public class DefaultMapper<T> implements CsvReaderMapper<T>, CsvWriterMapper<T>
         if (String.class == type) {
             return value;
         }
-        final Object primVal = Primitives.valueFor(type, value);
-        if (primVal != null) {
-            return primVal;
+
+        if (coercingConverter != null) {
+            final Object val = coercingConverter.valueFor(type, value);
+            if (val != null) {
+                return val;
+            }
         }
+
         throw new IllegalArgumentException("Unsupported type " + type);
     }
+
+    private static CoercingConverter loadConverter() {
+        try {
+            Thread.currentThread().getContextClassLoader().loadClass("org.apache.xbean.propertyeditor.PropertyEditors");
+            return XBeanConverter.INSTANCE;
+        } catch (ClassNotFoundException e) {
+            return Primitives.INSTANCE;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/6047c395/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/Primitives.java
----------------------------------------------------------------------
diff --git a/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/Primitives.java b/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/Primitives.java
index 3f1ea59..c550158 100644
--- a/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/Primitives.java
+++ b/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/Primitives.java
@@ -16,60 +16,68 @@
  */
 package org.apache.batchee.csv.mapper;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
 
-public enum Primitives {
-    BooleanType(Boolean.class, boolean.class, new Converter() {
-        @Override
-        public Object convert(final String o) {
-            return Boolean.parseBoolean(o);
-        }
-    }),
-    IntegerType(Integer.class, int.class, new Converter() {
-        @Override
-        public Object convert(final String o) {
-            return Integer.parseInt(o);
-        }
-    }),
-    ShortType(Short.class, short.class, new Converter() {
-        @Override
-        public Object convert(final String o) {
-            return Short.parseShort(o);
-        }
-    }),
-    LongType(Long.class, long.class, new Converter() {
-        @Override
-        public Object convert(final String o) {
-            return Long.parseLong(o);
-        }
-    }),
-    ByteType(Byte.class, byte.class, new Converter() {
-        @Override
-        public Object convert(final String o) {
-            return Byte.parseByte(o);
-        }
-    }),
-    FloatType(Float.class, float.class, new Converter() {
-        @Override
-        public Object convert(final String o) {
-            return Float.parseFloat(o);
-        }
-    }),
-    DoubleType(Double.class, double.class, new Converter() {
-        @Override
-        public Object convert(final String o) {
-            return Double.parseDouble(o);
-        }
-    }),
-    CharacterType(Character.class, char.class, new Converter() {
-        @Override
-        public Object convert(final String o) {
-            return o.length() == 0 ? null : o.charAt(0);
-        }
-    });
+public class Primitives implements CoercingConverter {
+    static {
+        register(Boolean.class, boolean.class, false, new Converter() {
+            @Override
+            public Object convert(final String o) {
+                return Boolean.parseBoolean(o);
+            }
+        });
+        register(Integer.class, int.class, (int) 0, new Converter() {
+            @Override
+            public Object convert(final String o) {
+                return Integer.parseInt(o);
+            }
+        });
+        register(Short.class, short.class, (short) 0, new Converter() {
+            @Override
+            public Object convert(final String o) {
+                return Short.parseShort(o);
+            }
+        });
+        register(Long.class, long.class, 0L, new Converter() {
+            @Override
+            public Object convert(final String o) {
+                return Long.parseLong(o);
+            }
+        });
+        register(Byte.class, byte.class, (byte) 0, new Converter() {
+            @Override
+            public Object convert(final String o) {
+                return Byte.parseByte(o);
+            }
+        });
+        register(Float.class, float.class, 0.f, new Converter() {
+            @Override
+            public Object convert(final String o) {
+                return Float.parseFloat(o);
+            }
+        });
+        register(Double.class, double.class, 0., new Converter() {
+            @Override
+            public Object convert(final String o) {
+                return Double.parseDouble(o);
+            }
+        });
+        register(Character.class, char.class, (char) 0, new Converter() {
+            @Override
+            public Object convert(final String o) {
+                return o.length() == 0 ? null : o.charAt(0);
+            }
+        });
+    }
+
+    public static final CoercingConverter INSTANCE = new Primitives();
 
     private static final class Mapping {
+        private static final Map<Class<?>, Method> PRIMITIVES = new HashMap<Class<?>, Method>();
+        private static final Map<Class<?>, Object> PRIMITIVE_DEFAULTS = new HashMap<Class<?>, Object>();
         private static final Map<Class<?>, Class<?>> WRAPPERS = new HashMap<Class<?>, Class<?>>();
         private static final Map<Class<?>, Converter> CONVERTERS = new HashMap<Class<?>, Converter>();
 
@@ -82,20 +90,56 @@ public enum Primitives {
         Object convert(String o);
     }
 
-    Primitives(final Class<?> wrapper, final Class<?> primitive, final Converter converter) {
+    public static Object primitiveDefaultValue(final Class<?> type) {
+        return Mapping.PRIMITIVE_DEFAULTS.get(type);
+    }
+
+    private static void register(final Class<?> wrapper, final Class<?> primitive, final Object defaultValue, final Converter converter) {
         Mapping.WRAPPERS.put(wrapper, primitive);
         Mapping.CONVERTERS.put(wrapper, converter);
+        Mapping.PRIMITIVE_DEFAULTS.put(primitive, defaultValue);
+        if (wrapper != Character.class) {
+            try {
+                Mapping.PRIMITIVES.put(primitive, wrapper.getMethod("valueOf", String.class));
+            } catch (final NoSuchMethodException e) {
+                throw new IllegalStateException(e);
+            }
+        }
     }
 
-    public static Object valueFor(final Class<?> type, final String value) {
+    @Override
+    public Object valueFor(final Class<?> type, final String value) {
+        final Method valueOf = Mapping.PRIMITIVES.get(type);
+        if (valueOf != null) {
+            if (value == null || value.trim().isEmpty()) {
+                return Mapping.PRIMITIVE_DEFAULTS.get(type);
+            }
+
+            try {
+                return valueOf.invoke(null, value);
+            } catch (final IllegalAccessException e) {
+                throw new IllegalArgumentException(e);
+            } catch (InvocationTargetException e) {
+                throw new IllegalArgumentException(e.getCause());
+            }
+        } else if (char.class == type) {
+            if (value == null || value.trim().isEmpty()) {
+                return Mapping.PRIMITIVE_DEFAULTS.get(type);
+            }
+            return value.charAt(0);
+        }
+
         if (value == null) {
             return null;
         }
+
         final Class<?> currentType = value.getClass();
         final Class<?> primitive = Mapping.WRAPPERS.get(currentType);
         if (primitive != null && type == primitive) {
             return Mapping.CONVERTERS.get(currentType).convert(value);
         }
+
+
         return null;
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/6047c395/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/XBeanConverter.java
----------------------------------------------------------------------
diff --git a/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/XBeanConverter.java b/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/XBeanConverter.java
new file mode 100644
index 0000000..50e10cd
--- /dev/null
+++ b/extensions/commons-csv/src/main/java/org/apache/batchee/csv/mapper/XBeanConverter.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.batchee.csv.mapper;
+
+import org.apache.xbean.propertyeditor.PropertyEditors;
+
+public final class XBeanConverter implements CoercingConverter {
+    public static final CoercingConverter INSTANCE = new XBeanConverter();
+
+    @Override
+    public Object valueFor(final Class<?> type, final String value) {
+        if (value == null || value.isEmpty()) {
+            final Object def = Primitives.primitiveDefaultValue(type);
+            if (def != null) {
+                return def;
+            }
+        }
+        if (value == null) {
+            return null;
+        }
+        return PropertyEditors.canConvert(type) ? PropertyEditors.getValue(type, value) : null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/6047c395/extensions/commons-csv/src/test/java/org/apache/batchee/csv/mapper/PrimitivesTest.java
----------------------------------------------------------------------
diff --git a/extensions/commons-csv/src/test/java/org/apache/batchee/csv/mapper/PrimitivesTest.java b/extensions/commons-csv/src/test/java/org/apache/batchee/csv/mapper/PrimitivesTest.java
new file mode 100644
index 0000000..971e282
--- /dev/null
+++ b/extensions/commons-csv/src/test/java/org/apache/batchee/csv/mapper/PrimitivesTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.batchee.csv.mapper;
+
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+@RunWith(Theories.class)
+public class PrimitivesTest {
+    @DataPoints
+    public static CoercingConverter[] converters() {
+        return new CoercingConverter[]{ new Primitives(), new XBeanConverter() };
+    }
+
+    @Theory
+    public void defaultValues(final CoercingConverter converter) {
+        assertEquals(0L, converter.valueFor(long.class, ""));
+        assertEquals((short) 0, converter.valueFor(short.class, ""));
+        assertEquals(0, converter.valueFor(int.class, ""));
+        assertEquals((byte) 0, converter.valueFor(byte.class, ""));
+        assertEquals((char) 0, converter.valueFor(char.class, ""));
+        assertEquals(0.f, converter.valueFor(float.class, ""));
+        assertEquals(0., converter.valueFor(double.class, ""));
+    }
+
+    @Theory
+    public void primitives(final CoercingConverter converter) {
+        assertEquals(1L, converter.valueFor(long.class, "1"));
+        assertEquals((short) 1, converter.valueFor(short.class, "1"));
+        assertEquals(1, converter.valueFor(int.class, "1"));
+        assertEquals((byte) 1, converter.valueFor(byte.class, "1"));
+        assertEquals(1.f, converter.valueFor(float.class, "1"));
+        assertEquals(1., converter.valueFor(double.class, "1"));
+        assertEquals('1', converter.valueFor(char.class, "1"));
+    }
+}