SLING-6420 Enhance Conversion Rules for ValueMapDecorator - unit tests and first enhancements

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1775365 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 414beb0..af674f8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,6 +85,12 @@
             <version>2.3.7</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.2</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/src/main/java/org/apache/sling/api/wrappers/ValueMapDecorator.java b/src/main/java/org/apache/sling/api/wrappers/ValueMapDecorator.java
index 0f941a1..72fa879 100644
--- a/src/main/java/org/apache/sling/api/wrappers/ValueMapDecorator.java
+++ b/src/main/java/org/apache/sling/api/wrappers/ValueMapDecorator.java
@@ -19,6 +19,7 @@
 package org.apache.sling.api.wrappers;
 
 import java.lang.reflect.Array;
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
@@ -66,28 +67,52 @@
      */
     @SuppressWarnings("unchecked")
     private <T> T convert(Object obj, Class<T> type) {
-        // todo: do smarter checks
-        try {
-            if (obj == null) {
-                return null;
-            } else if (type.isAssignableFrom(obj.getClass())) {
-                return (T) obj;
-            } else if (type.isArray()) {
-                return (T) convertToArray(obj, type.getComponentType());
-            } else if (type == String.class) {
-                return (T) String.valueOf(getSingleValue(obj));
-            } else if (type == Integer.class) {
-                return (T) (Integer) Integer.parseInt(getSingleValue(obj));
-            } else if (type == Long.class) {
-                return (T) (Long) Long.parseLong(getSingleValue(obj));
-            } else if (type == Double.class) {
-                return (T) (Double) Double.parseDouble(getSingleValue(obj));
-            } else if (type == Boolean.class) {
-                return (T) (Boolean) Boolean.parseBoolean(getSingleValue(obj));
-            } else {
+        if (obj == null) {
+            return null;
+        }
+        else if (type.isArray()) {
+            return (T) convertToArray(obj, type.getComponentType());
+        }
+        if (type.isAssignableFrom(obj.getClass())) {
+            return (T) obj;
+        }
+        else {
+            String result = getSingleValue(obj);
+            if (result == null) {
                 return null;
             }
-        } catch (NumberFormatException e) {
+            if (type == String.class) {
+                return (T) result.toString();
+            }
+            if (type == Boolean.class) {
+                return (T) (Boolean)Boolean.parseBoolean(result);
+            }
+            try {
+                if (type == Byte.class) {
+                    return (T) (Byte)Byte.parseByte(result);
+                }
+                if (type == Short.class) {
+                    return (T) (Short)Short.parseShort(result);
+                }
+                if (type == Integer.class) {
+                    return (T) (Integer)Integer.parseInt(result);
+                }
+                if (type == Long.class) {
+                    return (T) (Long)Long.parseLong(result);
+                }
+                if (type == Float.class) {
+                    return (T) (Float)Float.parseFloat(result);
+                }
+                if (type == Double.class) {
+                    return (T) (Double)Double.parseDouble(result);
+                }
+                if (type == BigDecimal.class) {
+                    return (T) new BigDecimal(result);
+                }
+            }
+            catch (NumberFormatException e) {
+                return null;
+            }
             return null;
         }
     }
@@ -103,8 +128,12 @@
         if (obj == null) {
             result = null;
         } else if (obj.getClass().isArray()) {
-            final Object[] values = (Object[]) obj;
-            result = values[0] != null ? values[0].toString() : null;
+            if (Array.getLength(obj) == 0) {
+                result = null;
+            }
+            else {
+                result = getSingleValue(Array.get(obj, 0));
+            }
         } else {
             result = obj.toString();
         }
@@ -120,10 +149,9 @@
     @SuppressWarnings("unchecked")
     private <T> T[] convertToArray(Object obj, Class<T> type) {
         if (obj.getClass().isArray()) {
-            final Object[] array = (Object[]) obj;
             List<Object> resultList = new ArrayList<Object>();
-            for (int i = 0; i < array.length; i++) {
-                T singleValueResult = convert(array[i], type);
+            for (int i = 0; i < Array.getLength(obj); i++) {
+                T singleValueResult = convert(Array.get(obj, i), type);
                 if (singleValueResult != null) {
                     resultList.add(singleValueResult);
                 }
diff --git a/src/test/java/org/apache/sling/api/wrappers/TestUtils.java b/src/test/java/org/apache/sling/api/wrappers/TestUtils.java
new file mode 100644
index 0000000..18c081b
--- /dev/null
+++ b/src/test/java/org/apache/sling/api/wrappers/TestUtils.java
@@ -0,0 +1,205 @@
+/*
+ * 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.sling.api.wrappers;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.lang.reflect.Array;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.ClassUtils;
+
+/**
+ * Tests all permutations of object conversions between single values and array types, and null handling.
+ */
+class TestUtils {
+    
+    @SuppressWarnings("unchecked")
+    public static class ConversionAssert<T,U> {
+        private final T input1;
+        private final T input2;
+        private Class<T> inputType;
+        private U expected1;
+        private U expected2;
+        private U nullValue;
+        private Class<U> expectedType;
+        
+        private ConversionAssert(T input1, T input2, boolean inputTypePrimitive) {
+            this.input1 = input1;
+            this.input2 = input2;
+            this.inputType = (Class<T>)input1.getClass();
+            if (inputTypePrimitive) {
+                this.inputType = (Class<T>)ClassUtils.wrapperToPrimitive(this.inputType);
+            }
+        }
+        
+        private void expected(U expected1, U expected2, boolean expectedTypePrimitive) {
+            this.expected1 = expected1;
+            this.expected2 = expected2;
+            this.expectedType = (Class<U>)expected1.getClass();
+            if (expectedTypePrimitive) {
+                expectedType = (Class<U>)ClassUtils.wrapperToPrimitive(this.expectedType);
+            }
+        }
+        
+        /**
+         * @param expected1 Singleton or first array expected result value
+         * @param expected2 Second array expected result value
+         * @return this
+         */
+        public ConversionAssert<T,U> to(U expected1, U expected2) {
+            expected(expected1, expected2, false);
+            return this;
+        }
+
+        /**
+         * @param expected1 Singleton or first array expected result value
+         * @param expected2 Second array expected result value
+         * @return this
+         */
+        public ConversionAssert<T,U> toPrimitive(U expected1, U expected2) {
+            expected(expected1, expected2, true);
+            return this;
+        }
+        
+        /**
+         * @param expected1 Singleton or first array expected result value
+         * @param expected2 Second array expected result value
+         * @return this
+         */
+        public ConversionAssert<T,U> toNull(Class<U> expectedType) {
+            expected1 = null;
+            expected2 = null;
+            this.expectedType = expectedType;
+            return this;
+        }
+        
+        /**
+         * @param nullValue Result value in case of null
+         */
+        public ConversionAssert<T,U> nullValue(U nullValue) {
+            this.nullValue = nullValue;
+            return this;
+        }
+        
+        /**
+         * Do assertion
+         */
+        public void test() {
+            Class<U[]> expectedArrayType = (Class<U[]>)Array.newInstance(this.expectedType, 0).getClass();
+            assertPermuations(input1, input2, inputType, expected1, expected2, nullValue, expectedType, expectedArrayType);
+        }
+    }
+
+    /**
+     * @param input1 Singleton or first array input value
+     * @param input2 Second array input value
+     */
+    public static <T,U> ConversionAssert<T,U> conv(T input1, T input2) {
+        return new ConversionAssert<T,U>(input1, input2, false);
+    }
+
+    /**
+     * @param input1 Singleton or first array input value
+     * @param input2 Second array input value
+     */
+    public static <T,U> ConversionAssert<T,U> convPrimitive(T input1, T input2) {
+        return new ConversionAssert<T,U>(input1, input2, true);
+    }
+
+    private static <T,U> void assertPermuations(T input1, T input2, Class<T> inputType,
+            U expected1, U expected2, U nullValue, Class<U> expectedType, Class<U[]> expectedArrayType) {
+        
+        // single value to single value
+        assertConversion(expected1, input1, expectedType);
+        
+        // single value to array
+        Object expectedSingletonArray = Array.newInstance(expectedType, 1);
+        Array.set(expectedSingletonArray, 0, expected1);
+        assertConversion(expectedSingletonArray, input1, expectedArrayType);
+        
+        // array to array
+        Object inputDoubleArray = Array.newInstance(inputType, 2);
+        Array.set(inputDoubleArray, 0, input1);
+        Array.set(inputDoubleArray, 1, input2);
+        Object expectedDoubleArray = Array.newInstance(expectedType, 2);
+        Array.set(expectedDoubleArray, 0,  expected1);
+        Array.set(expectedDoubleArray, 1,  expected2);
+        assertConversion(expectedDoubleArray, inputDoubleArray, expectedArrayType);
+        
+        // array to single (first) value
+        assertConversion(expected1, inputDoubleArray, expectedType);
+        
+        // null to single value
+        assertConversion(nullValue, null, expectedType);
+        
+        // null to array
+        assertConversion(null, null, expectedArrayType);
+        
+        // empty array to single value
+        Object inputEmptyArray = Array.newInstance(inputType, 0);
+        assertConversion(nullValue, inputEmptyArray, expectedType);
+
+        // empty array to array
+        assertConversion(null, inputEmptyArray, expectedArrayType);
+    }
+    
+    @SuppressWarnings("unchecked")
+    private static <T,U> void assertConversion(Object expected, Object input, Class<U> type) {
+        Map<String,Object> map = new HashMap<>();
+        map.put("key", input);
+        ValueMapDecorator underTest = new ValueMapDecorator(map);
+        U result = underTest.get("key", type);
+        String msg = "Convert '" + toString(input) + "' to " + type.getSimpleName();
+        if (expected == null) {
+            assertNull(msg, result);
+        }
+        else if (expected.getClass().isArray()) {
+            assertArrayEquals(msg, (U[])expected, (U[])result);
+        }
+        else {
+            assertEquals(msg, expected, result);
+        }
+    }
+    
+    private static String toString(Object input) {
+        if (input == null) {
+            return "null";
+        }
+        else if (input.getClass().isArray()) {
+            StringBuilder sb = new StringBuilder();
+            sb.append("[");
+            for (int i=0; i<Array.getLength(input); i++) {
+                if (i > 0) {
+                    sb.append(",");
+                }
+                sb.append(Array.get(input, i));
+            }
+            sb.append("]");
+            return sb.toString();
+        }
+        else {
+            return input.toString();
+        }
+    }
+    
+}
diff --git a/src/test/java/org/apache/sling/api/wrappers/ValueMapDecoratorConversionTest.java b/src/test/java/org/apache/sling/api/wrappers/ValueMapDecoratorConversionTest.java
new file mode 100644
index 0000000..1fd7c90
--- /dev/null
+++ b/src/test/java/org/apache/sling/api/wrappers/ValueMapDecoratorConversionTest.java
@@ -0,0 +1,348 @@
+/*
+ * 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.sling.api.wrappers;
+
+import java.math.BigDecimal;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.commons.lang3.time.DateUtils;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class ValueMapDecoratorConversionTest {
+    
+    private static final String STRING_1 = "item1";
+    private static final String STRING_2 = "item2";
+    private static final boolean BOOLEAN_1 = true;
+    private static final boolean BOOLEAN_2 = false;
+    private static final byte BYTE_1 = (byte)0x01;
+    private static final byte BYTE_2 = (byte)0x02;
+    private static final short SHORT_1 = (short)12;
+    private static final short SHORT_2 = (short)34;
+    private static final int INT_1 = 55;
+    private static final int INT_2 = -123;
+    private static final long LONG_1 = 1234L;
+    private static final long LONG_2 = -4567L;
+    private static final float FLOAT_1 = 1.23f;
+    private static final float FLOAT_2 = -4.56f;
+    private static final double DOUBLE_1 = 12.34d;
+    private static final double DOUBLE_2 = -45.67d;
+    private static final BigDecimal BIGDECIMAL_1 = new BigDecimal("12345.67");
+    private static final BigDecimal BIGDECIMAL_2 = new BigDecimal("-23456.78");
+    private static final Calendar CALENDAR_1 = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.US);
+    private static final Calendar CALENDAR_2 = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.US);
+    {
+        CALENDAR_1.set(2016, 10, 15, 8, 20, 30);
+        CALENDAR_2.set(2015, 6, 31, 19, 10, 20);
+    }
+    private static final Date DATE_1 = CALENDAR_1.getTime();
+    private static final Date DATE_2 = CALENDAR_2.getTime();
+
+    @Test
+    public void testToString() {
+        TestUtils.conv(STRING_1, STRING_2).to(STRING_1, STRING_2).test();
+        TestUtils.convPrimitive(BOOLEAN_1, BOOLEAN_2).to(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2)).test();
+        TestUtils.conv(BOOLEAN_1, BOOLEAN_2).to(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2)).test();
+        TestUtils.convPrimitive(BYTE_1, BYTE_2).to(Byte.toString(BYTE_1), Byte.toString(BYTE_2)).test();
+        TestUtils.conv(BYTE_1, BYTE_2).to(Byte.toString(BYTE_1), Byte.toString(BYTE_2)).test();
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).to(Short.toString(SHORT_1), Short.toString(SHORT_2)).test();
+        TestUtils.conv(SHORT_1, SHORT_2).to(Short.toString(SHORT_1), Short.toString(SHORT_2)).test();
+        TestUtils.convPrimitive(INT_1, INT_2).to(Integer.toString(INT_1), Integer.toString(INT_2)).test();
+        TestUtils.conv(INT_1, INT_2).to(Integer.toString(INT_1), Integer.toString(INT_2)).test();
+        TestUtils.convPrimitive(LONG_1, LONG_2).to(Long.toString(LONG_1), Long.toString(LONG_2)).test();
+        TestUtils.conv(LONG_1, LONG_2).to(Long.toString(LONG_1), Long.toString(LONG_2)).test();
+        TestUtils.convPrimitive(FLOAT_1, FLOAT_2).to(Float.toString(FLOAT_1), Float.toString(FLOAT_2)).test();
+        TestUtils.conv(FLOAT_1, FLOAT_2).to(Float.toString(FLOAT_1), Float.toString(FLOAT_2)).test();
+        TestUtils.convPrimitive(DOUBLE_1, DOUBLE_2).to(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2)).test();
+        TestUtils.conv(DOUBLE_1, DOUBLE_2).to(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2)).test();
+        TestUtils.conv(BIGDECIMAL_1, BIGDECIMAL_2).to(BIGDECIMAL_1.toString(), BIGDECIMAL_2.toString()).test();
+        TestUtils.conv(CALENDAR_1, CALENDAR_2).to(CALENDAR_1.toString(), CALENDAR_2.toString()).test();
+        TestUtils.conv(DATE_1, DATE_2).to(DATE_1.toString(), DATE_2.toString()).test();
+    }
+    
+    @Test
+    public void testToBoolean() {
+        TestUtils.convPrimitive(BOOLEAN_1, BOOLEAN_2).to(BOOLEAN_1, BOOLEAN_2).test();
+        TestUtils.conv(BOOLEAN_1, BOOLEAN_2).to(BOOLEAN_1, BOOLEAN_2).test();
+        TestUtils.conv(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2)).to(BOOLEAN_1, BOOLEAN_2).test();
+        
+        // test other types that should not be converted
+        // TODO: test is not successful yet
+        /*
+        TestUtils.<Integer,Boolean>conv(INT_1, INT_2).toNull(Boolean.class).test();
+        TestUtils.<Date,Boolean>conv(DATE_1, DATE_2).toNull(Boolean.class).test();
+        */
+    }
+    
+    @Test
+    @Ignore // TODO: support primitive
+    public void testToBooleanPrimitive() {
+        TestUtils.convPrimitive(BOOLEAN_1, BOOLEAN_2).toPrimitive(BOOLEAN_1, BOOLEAN_2).nullValue(false).test();
+        TestUtils.conv(BOOLEAN_1, BOOLEAN_2).toPrimitive(BOOLEAN_1, BOOLEAN_2).nullValue(false).test();
+        TestUtils.conv(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2)).toPrimitive(BOOLEAN_1, BOOLEAN_2).nullValue(false).test();
+        
+        // test other types that should not be converted
+        TestUtils.conv(INT_1, INT_2).toPrimitive(false,  false).nullValue(false).test();
+        TestUtils.conv(DATE_1, DATE_2).toPrimitive(false,  false).nullValue(false).test();
+    }
+    
+    @Test
+    public void testToByte() {
+        TestUtils.convPrimitive(BYTE_1, BYTE_2).to(BYTE_1, BYTE_2).test();
+        TestUtils.conv(BYTE_1, BYTE_2).to(BYTE_1, BYTE_2).test();
+        TestUtils.conv(Byte.toString(BYTE_1), Byte.toString(BYTE_2)).to(BYTE_1, BYTE_2).test();
+        
+        // test conversion from other number types
+        TestUtils.conv(INT_1, INT_2).to((byte)INT_1, (byte)INT_2).test();
+        TestUtils.convPrimitive(INT_1, INT_2).to((byte)INT_1, (byte)INT_2).test();
+
+        // test other types that should not be converted
+        // TODO: test is not successful yet
+        /*
+        TestUtils.<Date,Byte>conv(DATE_1, DATE_2).toNull(Byte.class).test();
+        */
+    }
+    
+    @Test
+    @Ignore // TODO: support primitive
+    public void testToBytePrimitive() {
+        TestUtils.convPrimitive(BYTE_1, BYTE_2).toPrimitive(BYTE_1, BYTE_2).nullValue(0).test();
+        TestUtils.conv(BYTE_1, BYTE_2).toPrimitive(BYTE_1, BYTE_2).nullValue(0).test();
+        TestUtils.conv(Byte.toString(BYTE_1), Byte.toString(BYTE_2)).toPrimitive(BYTE_1, BYTE_2).nullValue(0).test();
+
+        // test conversion from other number types
+        TestUtils.conv(INT_1, INT_2).toPrimitive((byte)INT_1, (byte)INT_2).test();
+        TestUtils.convPrimitive(INT_1, INT_2).toPrimitive((byte)INT_1, (byte)INT_2).test();
+
+        // test other types that should not be converted
+        TestUtils.conv(INT_1, INT_2).toPrimitive((byte)0, (byte)0).nullValue(false).test();
+    }
+    
+    @Test
+    public void testToShort() {
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).to(SHORT_1, SHORT_2).test();
+        TestUtils.conv(SHORT_1, SHORT_2).to(SHORT_1, SHORT_2).test();
+        TestUtils.conv(Short.toString(SHORT_1), Short.toString(SHORT_2)).to(SHORT_1, SHORT_2).test();
+        
+        // test conversion from other number types
+        TestUtils.conv(INT_1, INT_2).to((short)INT_1, (short)INT_2).test();
+        TestUtils.convPrimitive(INT_1, INT_2).to((short)INT_1, (short)INT_2).test();
+
+        // test other types that should not be converted
+        // TODO: test is not successful yet
+        /*
+        TestUtils.<Date,Short>conv(DATE_1, DATE_2).toNull(Short.class).test();
+        */
+    }
+    
+    @Test
+    @Ignore // TODO: support primitive
+    public void testToShortPrimitive() {
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).toPrimitive(SHORT_1, SHORT_2).nullValue(0).test();
+        TestUtils.conv(SHORT_1, SHORT_2).toPrimitive(SHORT_1, SHORT_2).nullValue(0).test();
+        TestUtils.conv(Short.toString(SHORT_1), Short.toString(SHORT_2)).toPrimitive(SHORT_1, SHORT_2).nullValue(0).test();
+
+        // test conversion from other number types
+        TestUtils.conv(INT_1, INT_2).toPrimitive((short)INT_1, (short)INT_2).test();
+        TestUtils.convPrimitive(INT_1, INT_2).toPrimitive((short)INT_1, (short)INT_2).test();
+
+        // test other types that should not be converted
+        TestUtils.conv(INT_1, INT_2).toPrimitive((short)0, (short)0).nullValue(false).test();
+    }
+    
+    @Test
+    public void testToInteger() {
+        TestUtils.convPrimitive(INT_1, INT_2).to(INT_1, INT_2).test();
+        TestUtils.conv(INT_1, INT_2).to(INT_1, INT_2).test();
+        TestUtils.conv(Integer.toString(INT_1), Integer.toString(INT_2)).to(INT_1, INT_2).test();
+        
+        // test conversion from other number types
+        TestUtils.conv(SHORT_1, SHORT_2).to((int)SHORT_1, (int)SHORT_2).test();
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).to((int)SHORT_1, (int)SHORT_2).test();
+
+        // test other types that should not be converted
+        // TODO: test is not successful yet
+        /*
+        TestUtils.<Date,Integer>conv(DATE_1, DATE_2).toNull(Integer.class).test();
+        */
+    }
+    
+    @Test
+    @Ignore // TODO: support primitive
+    public void testToIntegerPrimitive() {
+        TestUtils.convPrimitive(INT_1, INT_2).toPrimitive(INT_1, INT_2).nullValue(0).test();
+        TestUtils.conv(INT_1, INT_2).toPrimitive(INT_1, INT_2).nullValue(0).test();
+        TestUtils.conv(Integer.toString(INT_1), Integer.toString(INT_2)).toPrimitive(INT_1, INT_2).nullValue(0).test();
+
+        // test conversion from other number types
+        TestUtils.conv(SHORT_1, SHORT_2).toPrimitive((int)SHORT_1, (int)SHORT_2).test();
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).toPrimitive((int)SHORT_1, (int)SHORT_2).test();
+
+        // test other types that should not be converted
+        TestUtils.conv(INT_1, INT_2).toPrimitive((int)0, (int)0).nullValue(false).test();
+    }
+    
+    @Test
+    public void testToLong() {
+        TestUtils.convPrimitive(LONG_1, LONG_2).to(LONG_1, LONG_2).test();
+        TestUtils.conv(LONG_1, LONG_2).to(LONG_1, LONG_2).test();
+        TestUtils.conv(Long.toString(LONG_1), Long.toString(LONG_2)).to(LONG_1, LONG_2).test();
+        
+        // test conversion from other number types
+        TestUtils.conv(SHORT_1, SHORT_2).to((long)SHORT_1, (long)SHORT_2).test();
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).to((long)SHORT_1, (long)SHORT_2).test();
+
+        // test other types that should not be converted
+        // TODO: test is not successful yet
+        /*
+        TestUtils.<Date,Long>conv(DATE_1, DATE_2).toNull(Long.class).test();
+        */
+    }
+    
+    @Test
+    @Ignore // TODO: support primitive
+    public void testToLongPrimitive() {
+        TestUtils.convPrimitive(LONG_1, LONG_2).toPrimitive(LONG_1, LONG_2).nullValue(0).test();
+        TestUtils.conv(LONG_1, LONG_2).toPrimitive(LONG_1, LONG_2).nullValue(0).test();
+        TestUtils.conv(Long.toString(LONG_1), Long.toString(LONG_2)).toPrimitive(LONG_1, LONG_2).nullValue(0).test();
+
+        // test conversion from other number types
+        TestUtils.conv(SHORT_1, SHORT_2).toPrimitive((long)SHORT_1, (long)SHORT_2).test();
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).toPrimitive((long)SHORT_1, (long)SHORT_2).test();
+
+        // test other types that should not be converted
+        TestUtils.conv(LONG_1, LONG_2).toPrimitive((long)0, (long)0).nullValue(false).test();
+    }
+    
+    @Test
+    public void testToFloat() {
+        TestUtils.convPrimitive(FLOAT_1, FLOAT_2).to(FLOAT_1, FLOAT_2).test();
+        TestUtils.conv(FLOAT_1, FLOAT_2).to(FLOAT_1, FLOAT_2).test();
+        TestUtils.conv(Float.toString(FLOAT_1), Float.toString(FLOAT_2)).to(FLOAT_1, FLOAT_2).test();
+        
+        // test conversion from other number types
+        TestUtils.conv(SHORT_1, SHORT_2).to((float)SHORT_1, (float)SHORT_2).test();
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).to((float)SHORT_1, (float)SHORT_2).test();
+
+        // test other types that should not be converted
+        // TODO: test is not successful yet
+        /*
+        TestUtils.<Date,Float>conv(DATE_1, DATE_2).toNull(Float.class).test();
+        */
+    }
+    
+    @Test
+    @Ignore // TODO: support primitive
+    public void testToFloatPrimitive() {
+        TestUtils.convPrimitive(FLOAT_1, FLOAT_2).toPrimitive(FLOAT_1, FLOAT_2).nullValue(0).test();
+        TestUtils.conv(FLOAT_1, FLOAT_2).toPrimitive(FLOAT_1, FLOAT_2).nullValue(0).test();
+        TestUtils.conv(Float.toString(FLOAT_1), Float.toString(FLOAT_2)).toPrimitive(FLOAT_1, FLOAT_2).nullValue(0).test();
+
+        // test conversion from other number types
+        TestUtils.conv(SHORT_1, SHORT_2).toPrimitive((float)SHORT_1, (float)SHORT_2).test();
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).toPrimitive((float)SHORT_1, (float)SHORT_2).test();
+
+        // test other types that should not be converted
+        TestUtils.conv(FLOAT_1, FLOAT_2).toPrimitive((float)0, (float)0).nullValue(false).test();
+    }
+ 
+    @Test
+    public void testToDouble() {
+        TestUtils.convPrimitive(DOUBLE_1, DOUBLE_2).to(DOUBLE_1, DOUBLE_2).test();
+        TestUtils.conv(DOUBLE_1, DOUBLE_2).to(DOUBLE_1, DOUBLE_2).test();
+        TestUtils.conv(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2)).to(DOUBLE_1, DOUBLE_2).test();
+        
+        // test conversion from other number types
+        TestUtils.conv(SHORT_1, SHORT_2).to((double)SHORT_1, (double)SHORT_2).test();
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).to((double)SHORT_1, (double)SHORT_2).test();
+
+        // test other types that should not be converted
+        // TODO: test is not successful yet
+        /*
+        TestUtils.<Date,Double>conv(DATE_1, DATE_2).toNull(Double.class).test();
+        */
+    }
+    
+    @Test
+    @Ignore // TODO: support primitive
+    public void testToDoublePrimitive() {
+        TestUtils.convPrimitive(DOUBLE_1, DOUBLE_2).toPrimitive(DOUBLE_1, DOUBLE_2).nullValue(0).test();
+        TestUtils.conv(DOUBLE_1, DOUBLE_2).toPrimitive(DOUBLE_1, DOUBLE_2).nullValue(0).test();
+        TestUtils.conv(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2)).toPrimitive(DOUBLE_1, DOUBLE_2).nullValue(0).test();
+
+        // test conversion from other number types
+        TestUtils.conv(SHORT_1, SHORT_2).toPrimitive((double)SHORT_1, (double)SHORT_2).test();
+        TestUtils.convPrimitive(SHORT_1, SHORT_2).toPrimitive((double)SHORT_1, (double)SHORT_2).test();
+
+        // test other types that should not be converted
+        TestUtils.conv(DOUBLE_1, DOUBLE_2).toPrimitive((double)0, (double)0).nullValue(false).test();
+    }
+    
+    @Test
+    public void testToBigDecimal() {
+        TestUtils.conv(BIGDECIMAL_1, BIGDECIMAL_2).to(BIGDECIMAL_1, BIGDECIMAL_2).test();
+        TestUtils.conv(BIGDECIMAL_1.toString(), BIGDECIMAL_2.toString()).to(BIGDECIMAL_1, BIGDECIMAL_2).test();
+        
+        // test conversion from other number types
+        TestUtils.conv(LONG_1, LONG_2).to(BigDecimal.valueOf(LONG_1), BigDecimal.valueOf(LONG_2)).test();
+        TestUtils.convPrimitive(LONG_1, LONG_2).to(BigDecimal.valueOf(LONG_1), BigDecimal.valueOf(LONG_2)).test();
+        TestUtils.conv(DOUBLE_1, DOUBLE_2).to(BigDecimal.valueOf(DOUBLE_1), BigDecimal.valueOf(DOUBLE_2)).test();
+        TestUtils.convPrimitive(DOUBLE_1, DOUBLE_2).to(BigDecimal.valueOf(DOUBLE_1), BigDecimal.valueOf(DOUBLE_2)).test();
+
+        // test other types that should not be converted
+        // TODO: test is not successful yet
+        /*
+        TestUtils.<Date,BigDecimal>conv(DATE_1, DATE_2).toNull(BigDecimal.class).test();
+        */
+    }
+    
+    @Test
+    @Ignore // TODO: support calendar
+    public void testToCalendar() {
+        TestUtils.conv(CALENDAR_1, CALENDAR_2).to(CALENDAR_1, CALENDAR_2).test();
+        TestUtils.conv(CALENDAR_1.toString(), CALENDAR_2.toString()).to(CALENDAR_1, CALENDAR_2).test();
+        
+        // test conversion from other date types
+        TestUtils.conv(DATE_1, DATE_2).to(DateUtils.toCalendar(DATE_1), DateUtils.toCalendar(DATE_2)).test();
+        TestUtils.convPrimitive(DATE_1, DATE_2).to(DateUtils.toCalendar(DATE_1), DateUtils.toCalendar(DATE_2)).test();
+
+        // test other types that should not be converted
+        TestUtils.<String,Calendar>conv(STRING_1, STRING_2).toNull(Calendar.class).test();
+        TestUtils.<Boolean,Calendar>conv(BOOLEAN_1, BOOLEAN_2).toNull(Calendar.class).test();
+    }
+    
+    @Test
+    @Ignore // TODO: support date
+    public void testToDate() {
+        TestUtils.conv(DATE_1, DATE_2).to(DATE_1, DATE_2).test();
+        TestUtils.conv(DATE_1.toString(), DATE_2.toString()).to(DATE_1, DATE_2).test();
+        
+        // test conversion from other date types
+        TestUtils.conv(CALENDAR_1, CALENDAR_2).to(CALENDAR_1.getTime(), CALENDAR_2.getTime()).test();
+        TestUtils.convPrimitive(CALENDAR_1, CALENDAR_2).to(CALENDAR_1.getTime(), CALENDAR_2.getTime()).test();
+
+        // test other types that should not be converted
+        TestUtils.<String,Date>conv(STRING_1, STRING_2).toNull(Date.class).test();
+        TestUtils.<Boolean,Date>conv(BOOLEAN_1, BOOLEAN_2).toNull(Date.class).test();
+    }
+    
+}