Merge branch 'master' of git@github.com:apache/sling-org-apache-sling-api.git
diff --git a/pom.xml b/pom.xml
index 6579879..3ac5561 100644
--- a/pom.xml
+++ b/pom.xml
@@ -157,6 +157,29 @@
                     </excludes>
                 </configuration>
             </plugin>
+           <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <version>3.2.1</version>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <createSourcesJar>true</createSourcesJar>
+                            <shadeSourcesContent>true</shadeSourcesContent>
+                            <relocations>
+                                <relocation>
+                                    <pattern>org/apache/jackrabbit/util</pattern>
+                                    <shadedPattern>org.apache.sling.api.impl.jackrabbit</shadedPattern>
+                                </relocation>
+                            </relocations>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
     <profiles>
diff --git a/src/main/java/org/apache/sling/api/wrappers/impl/ObjectConverter.java b/src/main/java/org/apache/sling/api/wrappers/impl/ObjectConverter.java
index 8062149..060647a 100644
--- a/src/main/java/org/apache/sling/api/wrappers/impl/ObjectConverter.java
+++ b/src/main/java/org/apache/sling/api/wrappers/impl/ObjectConverter.java
@@ -19,10 +19,13 @@
 package org.apache.sling.api.wrappers.impl;
 
 import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.Calendar;
 import java.util.Date;
-import java.util.GregorianCalendar;
+import java.util.TimeZone;
 
+import org.apache.jackrabbit.util.ISO8601;
 import org.osgi.util.converter.ConversionException;
 import org.osgi.util.converter.Converter;
 import org.osgi.util.converter.Converters;
@@ -45,16 +48,19 @@
             synchronized (ObjectConverter.class) {
                 if (converter == null) {
                     converter = Converters.newConverterBuilder()
-                            .rule(new TypeRule<String, GregorianCalendar>(String.class, GregorianCalendar.class,
+                            .rule(new TypeRule<String, Calendar>(String.class, Calendar.class,
                                     ObjectConverter::toCalendar))
-                            .rule(new TypeRule<Date, GregorianCalendar>(Date.class, GregorianCalendar.class,
+                            .rule(new TypeRule<Date, Calendar>(Date.class, Calendar.class,
                                     ObjectConverter::toCalendar))
                             .rule(new TypeRule<String, Date>(String.class, Date.class, ObjectConverter::toDate))
                             .rule(new TypeRule<Calendar, String>(Calendar.class, String.class,
                                     ObjectConverter::toString))
                             .rule(new TypeRule<Date, String>(Date.class, String.class, ObjectConverter::toString))
-                            .rule(new TypeRule<GregorianCalendar, Date>(GregorianCalendar.class, Date.class,
+                            .rule(new TypeRule<Calendar, Date>(Calendar.class, Date.class,
                                     ObjectConverter::toDate))
+                            .rule(new TypeRule<>(Calendar.class, ZonedDateTime.class, ObjectConverter::toZonedDateTime))
+                            .rule(new TypeRule<ZonedDateTime, Calendar>(ZonedDateTime.class, Calendar.class, ObjectConverter::toCalendar))
+                            .rule(new TypeRule<ZonedDateTime, String>(ZonedDateTime.class, String.class, ObjectConverter::toString))
                             .build();
                 }
             }
@@ -62,6 +68,20 @@
         return converter;
     }
 
+    private static String toString(ZonedDateTime zonedDateTime) {
+        return DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(zonedDateTime);
+    }
+
+    private static Calendar toCalendar(ZonedDateTime zonedDateTime) {
+        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(zonedDateTime.getOffset()));
+        calendar.setTimeInMillis(zonedDateTime.toInstant().toEpochMilli());
+        return calendar;
+    }
+
+    private static ZonedDateTime toZonedDateTime(Calendar calendar) {
+        return ZonedDateTime.ofInstant(calendar.toInstant(), calendar.getTimeZone().toZoneId().normalized());
+    }
+
     private static String toString(Calendar cal) {
         return cal.toInstant().toString();
     }
@@ -70,16 +90,15 @@
         return cal.toInstant().toString();
     }
 
-    private static GregorianCalendar toCalendar(String date) {
-        Calendar response = Calendar.getInstance();
-        response.setTime(Date.from(Instant.parse(date)));
-        return (GregorianCalendar) response;
+    private static Calendar toCalendar(String date) {
+        ZonedDateTime zonedDateTime = ZonedDateTime.parse(date, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+        return toCalendar(zonedDateTime);
     }
 
-    private static GregorianCalendar toCalendar(Date date) {
+    private static Calendar toCalendar(Date date) {
         Calendar response = Calendar.getInstance();
         response.setTime(date);
-        return (GregorianCalendar) response;
+        return response;
     }
 
     private static Date toDate(String date) {
diff --git a/src/test/java/org/apache/sling/api/wrappers/ValueMapDecoratorTest.java b/src/test/java/org/apache/sling/api/wrappers/ValueMapDecoratorTest.java
index c573849..185f497 100644
--- a/src/test/java/org/apache/sling/api/wrappers/ValueMapDecoratorTest.java
+++ b/src/test/java/org/apache/sling/api/wrappers/ValueMapDecoratorTest.java
@@ -18,14 +18,20 @@
  */
 package org.apache.sling.api.wrappers;
 
+import static org.junit.Assert.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.TimeZone;
 
 import org.apache.sling.api.resource.ValueMap;
+import org.hamcrest.Matchers;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -166,5 +172,28 @@
         decorated.get("prop1", "defValue");
         verify(original, times(1)).get("prop1", "defValue");
     }
+
+    @Test
+    public void testGettingZonedDateTime() {
+        String dateAsIso8601 = "2019-07-04T14:05:37.123+02:00";
+        ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateAsIso8601, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
+        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone(zonedDateTime.getOffset()));
+        calendar.setTimeInMillis(zonedDateTime.toInstant().toEpochMilli());
+
+        map.put("dateAsIso8601", dateAsIso8601);
+        map.put("dateAsCalendar", calendar);
+        map.put("dateAsZonedDateTime", zonedDateTime);
+
+        assertThat("Conversion from \"dateAsIso8601\" to ZonedDateTime",
+                valueMap.get("dateAsIso8601", ZonedDateTime.class), Matchers.is(zonedDateTime));
+        assertThat("Conversion from \"dateAsCalendar\" to ZonedDateTime",
+                valueMap.get("dateAsCalendar", ZonedDateTime.class), Matchers.is(zonedDateTime));
+        assertThat("Conversion from \"dateAsZonedDateTime\" to ZonedDateTime",
+                valueMap.get("dateAsZonedDateTime", ZonedDateTime.class), Matchers.is(zonedDateTime));
+        assertThat("Conversion from \"dateAsZonedDateTime\" to Calendar",
+                valueMap.get("dateAsZonedDateTime", Calendar.class), Matchers.is(calendar));
+        assertThat("Conversion from \"dateAsZonedDateTime\" to String",
+                valueMap.get("dateAsZonedDateTime", String.class), Matchers.is(dateAsIso8601));
+    }
     
 }
diff --git a/src/test/java/org/apache/sling/api/wrappers/impl/Convert.java b/src/test/java/org/apache/sling/api/wrappers/impl/Convert.java
index f2a9b92..9507352 100644
--- a/src/test/java/org/apache/sling/api/wrappers/impl/Convert.java
+++ b/src/test/java/org/apache/sling/api/wrappers/impl/Convert.java
@@ -18,6 +18,8 @@
  */
 package org.apache.sling.api.wrappers.impl;
 
+import org.apache.commons.lang3.ClassUtils;
+
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
@@ -25,8 +27,7 @@
 import java.lang.reflect.Array;
 import java.util.Calendar;
 import java.util.Date;
-
-import org.apache.commons.lang3.ClassUtils;
+import java.util.GregorianCalendar;
 
 /**
  * Tests all permutations of object conversions between single values and array types, and null handling.
@@ -36,80 +37,60 @@
     private Convert() {
         // static methods only
     }
-    
-    @SuppressWarnings("unchecked")
-    public static class ConversionAssert<T,U> {
+
+    public static class ConversionInput<T> {
+
         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) {
+
+        private final Class<? super T> inputType;
+
+        private ConversionInput(T input1, T input2, Class<? super T> inputType) {
             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;
+            this.inputType = inputType;
         }
 
         /**
          * @param expected1 Singleton or first array expected result value
          * @param expected2 Second array expected result value
+         * @param expectedType The (super)class or interface used for the conversion.
          * @return this
          */
-        public ConversionAssert<T,U> toPrimitive(U expected1, U expected2) {
-            expected(expected1, expected2, true);
-            return this;
+        public <U> ConversionAssert<T, U> to(U expected1, U expected2, Class<? super U> expectedType) {
+            return new ConversionAssert<T, U>(input1, input2, inputType, expected1, expected2, expectedType);
         }
-        
-        /**
-         * @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;
+    }
+    
+    @SuppressWarnings("unchecked")
+    public static class ConversionAssert<T,U> {
+
+        private final T input1;
+        private final T input2;
+        private final Class<? super T> inputType;
+
+        private final U expected1;
+        private final U expected2;
+        private final Class<? super U> expectedType;
+        private final U nullValue;
+
+        private ConversionAssert(T input1, T input2, Class<? super T> inputType, U expected1, U expected2, Class<? super U> expectedType) {
+            this.input1 = input1;
+            this.input2 = input2;
+            this.inputType = inputType;
+
+            this.expected1 = expected1;
+            this.expected2 = expected2;
             this.expectedType = expectedType;
-            return this;
+            this.nullValue = null;
         }
-        
-        /**
-         * @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();
+            Class<? super U[]> expectedArrayType = (Class<? super U[]>)Array.newInstance(this.expectedType, 0).getClass();
             assertPermuations(input1, input2, inputType, expected1, expected2, nullValue, expectedType, expectedArrayType);
         }
     }
@@ -118,20 +99,12 @@
      * @param input1 Singleton or first array input value
      * @param input2 Second array input value
      */
-    public static <T,U> ConversionAssert<T,U> from(T input1, T input2) {
-        return new ConversionAssert<T,U>(input1, input2, false);
+    public static <T> ConversionInput<T> from(T input1, T input2, Class<? super T> inputType) {
+        return new ConversionInput<>(input1, input2, inputType);
     }
 
-    /**
-     * @param input1 Singleton or first array input value
-     * @param input2 Second array input value
-     */
-    public static <T,U> ConversionAssert<T,U> fromPrimitive(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) {
+    private static <T, U> void assertPermuations(T input1, T input2, Class<? super T> inputType,
+            U expected1, U expected2, U nullValue, Class<? super U> expectedType, Class<? super U[]> expectedArrayType) {
         
         // single value to single value
         assertConversion(expected1, input1, expectedType);
@@ -181,14 +154,14 @@
     }
     
     @SuppressWarnings("unchecked")
-    private static <T,U> void assertConversion(Object expected, Object input, Class<U> type) {
-        U result = ObjectConverter.convert(input, type);
-        String msg = "Convert '" + toString(input) + "' to " + type.getSimpleName();
+    private static <T,U> void assertConversion(Object expected, Object input, Class<U> expectedType) {
+        U result = ObjectConverter.convert(input, expectedType);
+        String msg = "Convert '" + toString(input) + "' to " + expectedType.getSimpleName();
         if (expected == null) {
             assertNull(msg, result);
         }
-        else if (expected.getClass().isArray()) {
-            assertArrayEquals(msg, (U[])toStringIfDate(expected), (U[])toStringIfDate(result));
+        else if (expectedType.isArray() && !expectedType.getComponentType().isPrimitive()) {
+            assertArrayEquals(msg, toStringIfDate((Object[]) expected), toStringIfDate((Object[]) result));
         }
         else {
             assertEquals(msg, toStringIfDate(expected), toStringIfDate(result));
@@ -212,31 +185,30 @@
             return sb.toString();
         }
         else {
-            return toStringIfDate(input).toString();
+            return toStringIfDate(input);
         }
     }
     
-    private static Object toStringIfDate(Object input) {
-        if (input == null) {
-            return null;
-        }
+    private static String toStringIfDate(Object input) {
         if (input instanceof Calendar) {
             return "(Calendar)" + ((Calendar)input).getTime().toInstant().toString();
         }
         if (input instanceof Date) {
             return "(Date)" + ((Date)input).toInstant().toString();
         }
-        if (input.getClass().isArray()) {
-            if (Calendar.class.isAssignableFrom(input.getClass().getComponentType())
-                    || input.getClass().getComponentType() == Date.class) {
-                Object[] resultArray = new String[Array.getLength(input)];
-                for (int i=0; i<Array.getLength(input); i++) {
-                    resultArray[i] = toStringIfDate(Array.get(input, i));
-                }
-                return resultArray;
+        return null;
+    }
+
+    private static String[] toStringIfDate(Object[] input) {
+        if (Calendar.class.isAssignableFrom(input.getClass().getComponentType())
+                || input.getClass().getComponentType() == Date.class) {
+            String[] resultArray = new String[Array.getLength(input)];
+            for (int i=0; i<Array.getLength(input); i++) {
+                resultArray[i] = toStringIfDate(Array.get(input, i));
             }
+            return resultArray;
         }
-        return input;
+        return null;
     }
         
 }
diff --git a/src/test/java/org/apache/sling/api/wrappers/impl/ObjectConverterTest.java b/src/test/java/org/apache/sling/api/wrappers/impl/ObjectConverterTest.java
index a7493fc..7eab819 100644
--- a/src/test/java/org/apache/sling/api/wrappers/impl/ObjectConverterTest.java
+++ b/src/test/java/org/apache/sling/api/wrappers/impl/ObjectConverterTest.java
@@ -60,24 +60,24 @@
 
     @Test
     public void testDateToString() {
-        Convert.from(STRING_1, STRING_2).to(STRING_1, STRING_2).test();
-        Convert.fromPrimitive(BOOLEAN_1, BOOLEAN_2).to(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2)).test();
-        Convert.from(BOOLEAN_1, BOOLEAN_2).to(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2)).test();
-        Convert.fromPrimitive(BYTE_1, BYTE_2).to(Byte.toString(BYTE_1), Byte.toString(BYTE_2)).test();
-        Convert.from(BYTE_1, BYTE_2).to(Byte.toString(BYTE_1), Byte.toString(BYTE_2)).test();
-        Convert.fromPrimitive(SHORT_1, SHORT_2).to(Short.toString(SHORT_1), Short.toString(SHORT_2)).test();
-        Convert.from(SHORT_1, SHORT_2).to(Short.toString(SHORT_1), Short.toString(SHORT_2)).test();
-        Convert.fromPrimitive(INT_1, INT_2).to(Integer.toString(INT_1), Integer.toString(INT_2)).test();
-        Convert.from(INT_1, INT_2).to(Integer.toString(INT_1), Integer.toString(INT_2)).test();
-        Convert.fromPrimitive(LONG_1, LONG_2).to(Long.toString(LONG_1), Long.toString(LONG_2)).test();
-        Convert.from(LONG_1, LONG_2).to(Long.toString(LONG_1), Long.toString(LONG_2)).test();
-        Convert.fromPrimitive(FLOAT_1, FLOAT_2).to(Float.toString(FLOAT_1), Float.toString(FLOAT_2)).test();
-        Convert.from(FLOAT_1, FLOAT_2).to(Float.toString(FLOAT_1), Float.toString(FLOAT_2)).test();
-        Convert.fromPrimitive(DOUBLE_1, DOUBLE_2).to(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2)).test();
-        Convert.from(DOUBLE_1, DOUBLE_2).to(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2)).test();
-        Convert.from(BIGDECIMAL_1, BIGDECIMAL_2).to(BIGDECIMAL_1.toString(), BIGDECIMAL_2.toString()).test();
-        Convert.from(CALENDAR_1, CALENDAR_2).to(calendarToString(CALENDAR_1), calendarToString(CALENDAR_2)).test();
-        Convert.from(DATE_1, DATE_2).to(calendarToString(toCalendar(DATE_1)), calendarToString(toCalendar(DATE_2))).test();
+        Convert.from(STRING_1, STRING_2, String.class).to(STRING_1, STRING_2, String.class).test();
+        Convert.from(BOOLEAN_1, BOOLEAN_2, boolean.class).to(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2), String.class).test();
+        Convert.from(BOOLEAN_1, BOOLEAN_2, Boolean.class).to(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2), String.class).test();
+        Convert.from(BYTE_1, BYTE_2, byte.class).to(Byte.toString(BYTE_1), Byte.toString(BYTE_2), String.class).test();
+        Convert.from(BYTE_1, BYTE_2, Byte.class).to(Byte.toString(BYTE_1), Byte.toString(BYTE_2), String.class).test();
+        Convert.from(SHORT_1, SHORT_2, short.class).to(Short.toString(SHORT_1), Short.toString(SHORT_2), String.class).test();
+        Convert.from(SHORT_1, SHORT_2, Short.class).to(Short.toString(SHORT_1), Short.toString(SHORT_2), String.class).test();
+        Convert.from(INT_1, INT_2, int.class).to(Integer.toString(INT_1), Integer.toString(INT_2), String.class).test();
+        Convert.from(INT_1, INT_2, Integer.class).to(Integer.toString(INT_1), Integer.toString(INT_2), String.class).test();
+        Convert.from(LONG_1, LONG_2, long.class).to(Long.toString(LONG_1), Long.toString(LONG_2), String.class).test();
+        Convert.from(LONG_1, LONG_2, Long.class).to(Long.toString(LONG_1), Long.toString(LONG_2), String.class).test();
+        Convert.from(FLOAT_1, FLOAT_2, float.class).to(Float.toString(FLOAT_1), Float.toString(FLOAT_2), String.class).test();
+        Convert.from(FLOAT_1, FLOAT_2, float.class).to(Float.toString(FLOAT_1), Float.toString(FLOAT_2), String.class).test();
+        Convert.from(DOUBLE_1, DOUBLE_2, double.class).to(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2), String.class).test();
+        Convert.from(DOUBLE_1, DOUBLE_2, Double.class).to(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2), String.class).test();
+        Convert.from(BIGDECIMAL_1, BIGDECIMAL_2, BigDecimal.class).to(BIGDECIMAL_1.toString(), BIGDECIMAL_2.toString(), String.class).test();
+        Convert.from(CALENDAR_1, CALENDAR_2, Calendar.class).to(calendarToString(CALENDAR_1), calendarToString(CALENDAR_2), String.class).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(calendarToString(toCalendar(DATE_1)), calendarToString(toCalendar(DATE_2)), String.class).test();
     }
     
     private String calendarToString(Calendar calendar) {
@@ -92,144 +92,144 @@
 
     @Test
     public void testToBoolean() {
-        Convert.fromPrimitive(BOOLEAN_1, BOOLEAN_2).to(BOOLEAN_1, BOOLEAN_2).test();
-        Convert.from(BOOLEAN_1, BOOLEAN_2).to(BOOLEAN_1, BOOLEAN_2).test();
-        Convert.from(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2)).to(BOOLEAN_1, BOOLEAN_2).test();
-        Convert.<Integer,Boolean>from(INT_1, INT_2).to(true,true).test();
-        Convert.<Integer,Boolean>from(1, 0).to(true,false).test();
-        Convert.<Date,Boolean>from(DATE_1, DATE_2).to(false,false).test();
+        Convert.from(BOOLEAN_1, BOOLEAN_2, boolean.class).to(BOOLEAN_1, BOOLEAN_2, Boolean.class).test();
+        Convert.from(BOOLEAN_1, BOOLEAN_2, Boolean.class).to(BOOLEAN_1, BOOLEAN_2, Boolean.class).test();
+        Convert.from(Boolean.toString(BOOLEAN_1), Boolean.toString(BOOLEAN_2), String.class).to(BOOLEAN_1, BOOLEAN_2, Boolean.class).test();
+        Convert.from(INT_1, INT_2, int.class).to(true, true, boolean.class).test();
+        Convert.from(1, 0, int.class).to(true, false, boolean.class).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(false, false, boolean.class).test();
     }
     
     @Test
     public void testToByte() {
-        Convert.fromPrimitive(BYTE_1, BYTE_2).to(BYTE_1, BYTE_2).test();
-        Convert.from(BYTE_1, BYTE_2).to(BYTE_1, BYTE_2).test();
-        Convert.from(Byte.toString(BYTE_1), Byte.toString(BYTE_2)).to(BYTE_1, BYTE_2).test();
+        Convert.from(BYTE_1, BYTE_2, byte.class).to(BYTE_1, BYTE_2, Byte.class).test();
+        Convert.from(BYTE_1, BYTE_2, Byte.class).to(BYTE_1, BYTE_2, Byte.class).test();
+        Convert.from(Byte.toString(BYTE_1), Byte.toString(BYTE_2), String.class).to(BYTE_1, BYTE_2, byte.class).test();
         
         // test conversion from other number types
-        Convert.from(INT_1, INT_2).to((byte)INT_1, (byte)INT_2).test();
-        Convert.fromPrimitive(INT_1, INT_2).to((byte)INT_1, (byte)INT_2).test();
+        Convert.from(INT_1, INT_2, Integer.class).to((byte)INT_1, (byte)INT_2, byte.class).test();
+        Convert.from(INT_1, INT_2, int.class).to((byte)INT_1, (byte)INT_2, Byte.class).test();
 
         // test other types that should not be converted
-        Convert.<Date,Byte>from(DATE_1, DATE_2).toNull(Byte.class).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(null, null, Byte.class).test();
     }
     
     @Test
     public void testToShort() {
-        Convert.fromPrimitive(SHORT_1, SHORT_2).to(SHORT_1, SHORT_2).test();
-        Convert.from(SHORT_1, SHORT_2).to(SHORT_1, SHORT_2).test();
-        Convert.from(Short.toString(SHORT_1), Short.toString(SHORT_2)).to(SHORT_1, SHORT_2).test();
+        Convert.from(SHORT_1, SHORT_2, short.class).to(SHORT_1, SHORT_2, Short.class).test();
+        Convert.from(SHORT_1, SHORT_2, Short.class).to(SHORT_1, SHORT_2, short.class).test();
+        Convert.from(Short.toString(SHORT_1), Short.toString(SHORT_2), String.class).to(SHORT_1, SHORT_2, short.class).test();
         
         // test conversion from other number types
-        Convert.from(INT_1, INT_2).to((short)INT_1, (short)INT_2).test();
-        Convert.fromPrimitive(INT_1, INT_2).to((short)INT_1, (short)INT_2).test();
+        Convert.from(INT_1, INT_2, Integer.class).to((short)INT_1, (short)INT_2, short.class).test();
+        Convert.from(INT_1, INT_2, int.class).to((short)INT_1, (short)INT_2, Short.class).test();
 
         // test other types that should not be converted
-        Convert.<Date,Short>from(DATE_1, DATE_2).toNull(Short.class).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(null, null, Short.class).test();
     }
     
     @Test
     public void testToInteger() {
-        Convert.fromPrimitive(INT_1, INT_2).to(INT_1, INT_2).test();
-        Convert.from(INT_1, INT_2).to(INT_1, INT_2).test();
-        Convert.from(Integer.toString(INT_1), Integer.toString(INT_2)).to(INT_1, INT_2).test();
+        Convert.from(INT_1, INT_2, int.class).to(INT_1, INT_2, int.class).test();
+        Convert.from(INT_1, INT_2, Integer.class).to(INT_1, INT_2, int.class).test();
+        Convert.from(Integer.toString(INT_1), Integer.toString(INT_2), String.class).to(INT_1, INT_2, int.class).test();
         
         // test conversion from other number types
-        Convert.from(SHORT_1, SHORT_2).to((int)SHORT_1, (int)SHORT_2).test();
-        Convert.fromPrimitive(SHORT_1, SHORT_2).to((int)SHORT_1, (int)SHORT_2).test();
+        Convert.from(SHORT_1, SHORT_2, Short.class).to((int)SHORT_1, (int)SHORT_2, Integer.class).test();
+        Convert.from(SHORT_1, SHORT_2, short.class).to((int)SHORT_1, (int)SHORT_2, int.class).test();
 
         // test other types that should not be converted
-        Convert.<Date,Integer>from(DATE_1, DATE_2).toNull(Integer.class).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(null, null, Integer.class).test();
     }
     
     @Test
     public void testToLong() {
-        Convert.fromPrimitive(LONG_1, LONG_2).to(LONG_1, LONG_2).test();
-        Convert.from(LONG_1, LONG_2).to(LONG_1, LONG_2).test();
-        Convert.from(Long.toString(LONG_1), Long.toString(LONG_2)).to(LONG_1, LONG_2).test();
+        Convert.from(LONG_1, LONG_2, long.class).to(LONG_1, LONG_2, long.class).test();
+        Convert.from(LONG_1, LONG_2, Long.class).to(LONG_1, LONG_2, Long.class).test();
+        Convert.from(Long.toString(LONG_1), Long.toString(LONG_2), String.class).to(LONG_1, LONG_2, long.class).test();
         
         // test conversion from other number types
-        Convert.from(SHORT_1, SHORT_2).to((long)SHORT_1, (long)SHORT_2).test();
-        Convert.fromPrimitive(SHORT_1, SHORT_2).to((long)SHORT_1, (long)SHORT_2).test();
+        Convert.from(SHORT_1, SHORT_2, Short.class).to((long)SHORT_1, (long)SHORT_2, long.class).test();
+        Convert.from(SHORT_1, SHORT_2, short.class).to((long)SHORT_1, (long)SHORT_2, Long.class).test();
 
         // test conversion from DATE to LONG
-        Convert.<Date,Long>from(DATE_1, DATE_2).to(DATE_1.getTime(), DATE_2.getTime()).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(DATE_1.getTime(), DATE_2.getTime(), long.class).test();
     }
     
     @Test
     public void testToFloat() {
-        Convert.fromPrimitive(FLOAT_1, FLOAT_2).to(FLOAT_1, FLOAT_2).test();
-        Convert.from(FLOAT_1, FLOAT_2).to(FLOAT_1, FLOAT_2).test();
-        Convert.from(Float.toString(FLOAT_1), Float.toString(FLOAT_2)).to(FLOAT_1, FLOAT_2).test();
+        Convert.from(FLOAT_1, FLOAT_2, float.class).to(FLOAT_1, FLOAT_2, float.class).test();
+        Convert.from(FLOAT_1, FLOAT_2, Float.class).to(FLOAT_1, FLOAT_2, float.class).test();
+        Convert.from(Float.toString(FLOAT_1), Float.toString(FLOAT_2), String.class).to(FLOAT_1, FLOAT_2, float.class).test();
         
         // test conversion from other number types
-        Convert.from(SHORT_1, SHORT_2).to((float)SHORT_1, (float)SHORT_2).test();
-        Convert.fromPrimitive(SHORT_1, SHORT_2).to((float)SHORT_1, (float)SHORT_2).test();
+        Convert.from(SHORT_1, SHORT_2, Short.class).to((float)SHORT_1, (float)SHORT_2, Float.class).test();
+        Convert.from(SHORT_1, SHORT_2, short.class).to((float)SHORT_1, (float)SHORT_2, float.class).test();
 
         // test other types that should not be converted
-        Convert.<Date,Float>from(DATE_1, DATE_2).toNull(Float.class).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(null, null, Float.class).test();
     }
     
     @Test
     public void testToDouble() {
-        Convert.fromPrimitive(DOUBLE_1, DOUBLE_2).to(DOUBLE_1, DOUBLE_2).test();
-        Convert.from(DOUBLE_1, DOUBLE_2).to(DOUBLE_1, DOUBLE_2).test();
-        Convert.from(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2)).to(DOUBLE_1, DOUBLE_2).test();
+        Convert.from(DOUBLE_1, DOUBLE_2, double.class).to(DOUBLE_1, DOUBLE_2, double.class).test();
+        Convert.from(DOUBLE_1, DOUBLE_2, Double.class).to(DOUBLE_1, DOUBLE_2, Double.class).test();
+        Convert.from(Double.toString(DOUBLE_1), Double.toString(DOUBLE_2), String.class).to(DOUBLE_1, DOUBLE_2, double.class).test();
         
         // test conversion from other number types
-        Convert.from(SHORT_1, SHORT_2).to((double)SHORT_1, (double)SHORT_2).test();
-        Convert.fromPrimitive(SHORT_1, SHORT_2).to((double)SHORT_1, (double)SHORT_2).test();
+        Convert.from(SHORT_1, SHORT_2, Short.class).to((double)SHORT_1, (double)SHORT_2, Double.class).test();
+        Convert.from(SHORT_1, SHORT_2, short.class).to((double)SHORT_1, (double)SHORT_2, double.class).test();
 
         // test other types that should not be converted
-        Convert.<Date,Double>from(DATE_1, DATE_2).toNull(Double.class).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(null, null, Double.class).test();
     }
     
     @Test
     public void testToBigDecimal() {
-        Convert.from(BIGDECIMAL_1, BIGDECIMAL_2).to(BIGDECIMAL_1, BIGDECIMAL_2).test();
-        Convert.from(BIGDECIMAL_1.toString(), BIGDECIMAL_2.toString()).to(BIGDECIMAL_1, BIGDECIMAL_2).test();
+        Convert.from(BIGDECIMAL_1, BIGDECIMAL_2, BigDecimal.class).to(BIGDECIMAL_1, BIGDECIMAL_2, BigDecimal.class).test();
+        Convert.from(BIGDECIMAL_1.toString(), BIGDECIMAL_2.toString(), String.class).to(BIGDECIMAL_1, BIGDECIMAL_2, BigDecimal.class).test();
         
         // test conversion from other number types
-        Convert.from(LONG_1, LONG_2).to(BigDecimal.valueOf(LONG_1), BigDecimal.valueOf(LONG_2)).test();
-        Convert.fromPrimitive(LONG_1, LONG_2).to(BigDecimal.valueOf(LONG_1), BigDecimal.valueOf(LONG_2)).test();
-        Convert.from(DOUBLE_1, DOUBLE_2).to(BigDecimal.valueOf(DOUBLE_1), BigDecimal.valueOf(DOUBLE_2)).test();
-        Convert.fromPrimitive(DOUBLE_1, DOUBLE_2).to(BigDecimal.valueOf(DOUBLE_1), BigDecimal.valueOf(DOUBLE_2)).test();
+        Convert.from(LONG_1, LONG_2, Long.class).to(BigDecimal.valueOf(LONG_1), BigDecimal.valueOf(LONG_2), BigDecimal.class).test();
+        Convert.from(LONG_1, LONG_2, Long.class).to(BigDecimal.valueOf(LONG_1), BigDecimal.valueOf(LONG_2), BigDecimal.class).test();
+        Convert.from(DOUBLE_1, DOUBLE_2, Double.class).to(BigDecimal.valueOf(DOUBLE_1), BigDecimal.valueOf(DOUBLE_2), BigDecimal.class).test();
+        Convert.from(DOUBLE_1, DOUBLE_2, double.class).to(BigDecimal.valueOf(DOUBLE_1), BigDecimal.valueOf(DOUBLE_2), BigDecimal.class).test();
 
         // test other types that should not be converted
-        Convert.<Date,BigDecimal>from(DATE_1, DATE_2).toNull(BigDecimal.class).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(null, null, BigDecimal.class).test();
     }
     
     @Test
     public void testToCalendar() {
-        Convert.from(CALENDAR_1, CALENDAR_2).to(CALENDAR_1, CALENDAR_2).test();
-        Convert.from(calendarToString(CALENDAR_1), calendarToString(CALENDAR_2)).to(CALENDAR_1, CALENDAR_2).test();
+        Convert.from(CALENDAR_1, CALENDAR_2, Calendar.class).to(CALENDAR_1, CALENDAR_2, Calendar.class).test();
+        Convert.from(calendarToString(CALENDAR_1), calendarToString(CALENDAR_2), String.class).to(CALENDAR_1, CALENDAR_2, Calendar.class).test();
         
         // test conversion from other date types
-        Convert.from(DATE_1, DATE_2).to(toCalendar(DATE_1), toCalendar(DATE_2)).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(toCalendar(DATE_1), toCalendar(DATE_2), Calendar.class).test();
 
         // test other types that should not be converted
-        Convert.<String,Calendar>from(STRING_1, STRING_2).toNull(Calendar.class).test();
-        Convert.<Boolean,Calendar>from(BOOLEAN_1, BOOLEAN_2).toNull(Calendar.class).test();
+        Convert.from(STRING_1, STRING_2, String.class).to(null, null, Calendar.class).test();
+        Convert.from(BOOLEAN_1, BOOLEAN_2, Boolean.class).to(null, null, Calendar.class).test();
     }
     
     @Test
     public void testToDate() {
-        Convert.from(DATE_1, DATE_2).to(DATE_1, DATE_2).test();
-        Convert.from(dateToString(DATE_1), dateToString(DATE_2)).to(DATE_1, DATE_2).test();
+        Convert.from(DATE_1, DATE_2, Date.class).to(DATE_1, DATE_2, Date.class).test();
+        Convert.from(dateToString(DATE_1), dateToString(DATE_2), String.class).to(DATE_1, DATE_2, Date.class).test();
         
         // test conversion from other date types
-        Convert.from(CALENDAR_1, CALENDAR_2).to(toDate(CALENDAR_1), toDate(CALENDAR_2)).test();
+        Convert.from(CALENDAR_1, CALENDAR_2, Calendar.class).to(toDate(CALENDAR_1), toDate(CALENDAR_2), Date.class).test();
 
         // test other types that should not be converted
-        Convert.<String,Date>from(STRING_1, STRING_2).toNull(Date.class).test();
-        Convert.<Boolean,Date>from(BOOLEAN_1, BOOLEAN_2).toNull(Date.class).test();
+        Convert.from(STRING_1, STRING_2, String.class).to(null, null, Date.class).test();
+        Convert.from(BOOLEAN_1, BOOLEAN_2, Boolean.class).to(null, null, Date.class).test();
     }
     
-    private Object toDate(Calendar calendar1) {
+    private Date toDate(Calendar calendar1) {
         return calendar1.getTime();
     }
 
-    private Object dateToString(Date date1) {
+    private String dateToString(Date date1) {
         return date1.toInstant().toString();
     }