You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ss...@apache.org on 2016/12/21 12:52:51 UTC

svn commit: r1775410 - in /sling/trunk/bundles/api: ./ src/main/java/org/apache/sling/api/wrappers/impl/ src/test/java/org/apache/sling/api/wrappers/impl/

Author: sseifert
Date: Wed Dec 21 12:52:50 2016
New Revision: 1775410

URL: http://svn.apache.org/viewvc?rev=1775410&view=rev
Log:
SLING-6420 support Calendar and Date

Added:
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/DateUtils.java   (with props)
Modified:
    sling/trunk/bundles/api/pom.xml
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/ObjectConverter.java
    sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/Convert.java
    sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/ObjectConverterTest.java

Modified: sling/trunk/bundles/api/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/pom.xml?rev=1775410&r1=1775409&r2=1775410&view=diff
==============================================================================
--- sling/trunk/bundles/api/pom.xml (original)
+++ sling/trunk/bundles/api/pom.xml Wed Dec 21 12:52:50 2016
@@ -52,6 +52,7 @@
     <properties>
         <site.jira.version.id>12314252</site.jira.version.id>
         <sling.java.version>7</sling.java.version>
+        <jackrabbit.version>2.13.4</jackrabbit.version>
     </properties>
 
     <dependencies>
@@ -68,6 +69,13 @@
             <groupId>com.google.code.findbugs</groupId>
             <artifactId>jsr305</artifactId>
         </dependency>
+        <!-- No external dependency, we embed some util classes -->
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit-jcr-commons</artifactId>
+            <version>${jackrabbit.version}</version>
+            <scope>provided</scope>
+        </dependency>
         <!-- Testing -->
         <dependency>
             <groupId>junit</groupId>
@@ -105,6 +113,10 @@
                         <Bundle-DocURL>
                             http://sling.apache.org/site/sling-api.html
                         </Bundle-DocURL>
+                        <!-- Include utility classes from Jackrabbit JCR Commons -->
+                        <Embed-Dependency>
+                           jackrabbit-jcr-commons;inline="org/apache/jackrabbit/util/ISO8601.*"
+                        </Embed-Dependency>
                     </instructions>
                 </configuration>
             </plugin>

Added: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/DateUtils.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/DateUtils.java?rev=1775410&view=auto
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/DateUtils.java (added)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/DateUtils.java Wed Dec 21 12:52:50 2016
@@ -0,0 +1,97 @@
+/*
+ * 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.impl;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.apache.jackrabbit.util.ISO8601;
+
+/**
+ * Date/Calendar utility functions.
+ */
+final class DateUtils {
+    
+    private DateUtils() {
+        // static methods only
+    }
+
+    /**
+     * @param date Date value
+     * @return Calendar value or null
+     */
+    public static Calendar toCalendar(Date input) {
+        if (input == null) {
+            return null;
+        }
+        Calendar result = Calendar.getInstance();
+        result.setTime(input);
+        return result;
+    }
+
+    /**
+     * @param calendar Calendar value
+     * @return Date value or null
+     */
+    public static Date toDate(Calendar input) {
+        if (input == null) {
+            return null;
+        }
+        return input.getTime();
+    }
+    
+    /**
+     * @param input Date value
+     * @return ISO8601 string representation or null
+     */
+    public static String dateToString(Date input) {
+        return calendarToString(toCalendar(input));
+    }
+
+    /**
+     * @param input Calendar value
+     * @return ISO8601 string representation or null
+     */
+    public static String calendarToString(Calendar input) {
+        if (input == null) {
+            return null;
+        }
+        return ISO8601.format(input);
+    }
+
+    /**
+     * @param input ISO8601 string representation
+     * @return Date value or null
+     */
+    public static Date dateFromString(String input) {
+        return toDate(calendarFromString(input));
+    }
+
+    /**
+     * @param input ISO8601 string representation
+     * @return Calendar value or null
+     */
+    public static Calendar calendarFromString(String input) {
+        if (input == null) {
+            return null;
+        }
+        return ISO8601.parse(input);
+    }
+
+}

Propchange: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/DateUtils.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/DateUtils.java
------------------------------------------------------------------------------
--- svn:keywords (added)
+++ svn:keywords Wed Dec 21 12:52:50 2016
@@ -0,0 +1 @@
+LastChangedDate LastChangedRevision LastChangedBy HeadURL Id Author

Propchange: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/DateUtils.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/ObjectConverter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/ObjectConverter.java?rev=1775410&r1=1775409&r2=1775410&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/ObjectConverter.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/wrappers/impl/ObjectConverter.java Wed Dec 21 12:52:50 2016
@@ -21,6 +21,8 @@ package org.apache.sling.api.wrappers.im
 import java.lang.reflect.Array;
 import java.math.BigDecimal;
 import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -43,51 +45,69 @@ public final class ObjectConverter {
         if (obj == null) {
             return null;
         }
-        else if (type.isArray()) {
-            return (T) convertToArray(obj, type.getComponentType());
+        
+        // convert array elements individually
+        if (type.isArray()) {
+            return (T)convertToArray(obj, type.getComponentType());
         }
+        
+        // check if direct assignment is possible
         if (type.isAssignableFrom(obj.getClass())) {
-            return (T) obj;
+            return (T)obj;
         }
-        else {
-            String result = getSingleValue(obj);
-            if (result == null) {
-                return null;
+        
+        // convert Calendar in Date and vice versa
+        if (Calendar.class.isAssignableFrom(type) && obj instanceof Date) {
+            return (T)DateUtils.toCalendar((Date)obj);
+        }
+        if (type == Date.class && obj instanceof Calendar) {
+            return (T)DateUtils.toDate((Calendar)obj);
+        }
+
+        // no direct conversion - format to string and try to parse to target type 
+        String result = getSingleValue(obj);
+        if (result == null) {
+            return null;
+        }
+        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 == String.class) {
-                return (T) result.toString();
+            if (type == Short.class) {
+                return (T)(Short)Short.parseShort(result);
             }
-            if (type == Boolean.class) {
-                return (T) (Boolean)Boolean.parseBoolean(result);
+            if (type == Integer.class) {
+                return (T)(Integer)Integer.parseInt(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);
-                }
+            if (type == Long.class) {
+                return (T)(Long)Long.parseLong(result);
             }
-            catch (NumberFormatException e) {
-                return null;
+            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;
         }
+        if (Calendar.class.isAssignableFrom(type)) {
+            return (T)DateUtils.calendarFromString(result);
+        }
+        if (type == Date.class) {
+            return (T)DateUtils.dateFromString(result);
+        }
+        return null;
     }
 
     /**
@@ -109,6 +129,12 @@ public final class ObjectConverter {
                 result = getSingleValue(Array.get(obj, 0));
             }
         }
+        else if (obj instanceof Calendar) {
+            result = DateUtils.calendarToString((Calendar)obj);
+        }
+        else if (obj instanceof Date) {
+            result = DateUtils.dateToString((Date)obj);
+        }
         else {
             result = obj.toString();
         }
@@ -147,5 +173,5 @@ public final class ObjectConverter {
             return arrayResult;
         }
     }
-
+    
 }

Modified: sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/Convert.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/Convert.java?rev=1775410&r1=1775409&r2=1775410&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/Convert.java (original)
+++ sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/Convert.java Wed Dec 21 12:52:50 2016
@@ -23,6 +23,8 @@ import static org.junit.Assert.assertEqu
 import static org.junit.Assert.assertNull;
 
 import java.lang.reflect.Array;
+import java.util.Calendar;
+import java.util.Date;
 
 import org.apache.commons.lang3.ClassUtils;
 
@@ -131,18 +133,28 @@ class Convert {
         assertConversion(expected1, input1, expectedType);
         
         // single value to array
-        Object expectedSingletonArray = Array.newInstance(expectedType, 1);
-        Array.set(expectedSingletonArray, 0, expected1);
-        assertConversion(expectedSingletonArray, input1, expectedArrayType);
+        if (expected1 == null && expected2 == null) {
+            assertConversion(nullValue, input1, expectedArrayType);
+        }
+        else {
+            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);
+        if (expected1 == null && expected2 == null) {
+            assertConversion(null, inputDoubleArray, expectedArrayType);
+        }
+        else {
+            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);
@@ -169,10 +181,10 @@ class Convert {
             assertNull(msg, result);
         }
         else if (expected.getClass().isArray()) {
-            assertArrayEquals(msg, (U[])expected, (U[])result);
+            assertArrayEquals(msg, (U[])toStringIfDate(expected), (U[])toStringIfDate(result));
         }
         else {
-            assertEquals(msg, expected, result);
+            assertEquals(msg, toStringIfDate(expected), toStringIfDate(result));
         }
     }
     
@@ -187,14 +199,37 @@ class Convert {
                 if (i > 0) {
                     sb.append(",");
                 }
-                sb.append(Array.get(input, i));
+                sb.append(toString(Array.get(input, i)));
             }
             sb.append("]");
             return sb.toString();
         }
         else {
-            return input.toString();
+            return toStringIfDate(input).toString();
         }
     }
     
+    private static Object toStringIfDate(Object input) {
+        if (input == null) {
+            return null;
+        }
+        if (input instanceof Calendar) {
+            return "(Calendar)" + DateUtils.calendarToString((Calendar)input);
+        }
+        if (input instanceof Date) {
+            return "(Date)" + DateUtils.dateToString((Date)input);
+        }
+        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 input;
+    }
+        
 }

Modified: sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/ObjectConverterTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/ObjectConverterTest.java?rev=1775410&r1=1775409&r2=1775410&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/ObjectConverterTest.java (original)
+++ sling/trunk/bundles/api/src/test/java/org/apache/sling/api/wrappers/impl/ObjectConverterTest.java Wed Dec 21 12:52:50 2016
@@ -18,13 +18,16 @@
  */
 package org.apache.sling.api.wrappers.impl;
 
+import static org.apache.sling.api.wrappers.impl.DateUtils.calendarToString;
+import static org.apache.sling.api.wrappers.impl.DateUtils.toCalendar;
+import static org.apache.sling.api.wrappers.impl.DateUtils.toDate;
+
 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;
 
@@ -52,32 +55,34 @@ public class ObjectConverterTest {
     private static final Calendar CALENDAR_2 = Calendar.getInstance(TimeZone.getTimeZone("GMT"), Locale.US);
     {
         CALENDAR_1.set(2016, 10, 15, 8, 20, 30);
+        CALENDAR_1.setLenient(true);
         CALENDAR_2.set(2015, 6, 31, 19, 10, 20);
+        CALENDAR_2.setLenient(true);
     }
-    private static final Date DATE_1 = CALENDAR_1.getTime();
-    private static final Date DATE_2 = CALENDAR_2.getTime();
+    private static final Date DATE_1 = toDate(CALENDAR_1);
+    private static final Date DATE_2 = toDate(CALENDAR_2);
 
     @Test
-    public void testToString() {
-        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(CALENDAR_1.toString(), CALENDAR_2.toString()).test();
-        Convert.from(DATE_1, DATE_2).to(DATE_1.toString(), DATE_2.toString()).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();
+            }
     
     @Test
     public void testToBoolean() {
@@ -316,14 +321,12 @@ public class ObjectConverterTest {
     }
     
     @Test
-    @Ignore // TODO: support calendar
     public void testToCalendar() {
         Convert.from(CALENDAR_1, CALENDAR_2).to(CALENDAR_1, CALENDAR_2).test();
-        Convert.from(CALENDAR_1.toString(), CALENDAR_2.toString()).to(CALENDAR_1, CALENDAR_2).test();
+        Convert.from(DateUtils.calendarToString(CALENDAR_1), DateUtils.calendarToString(CALENDAR_2)).to(CALENDAR_1, CALENDAR_2).test();
         
         // test conversion from other date types
-        Convert.from(DATE_1, DATE_2).to(DateUtils.toCalendar(DATE_1), DateUtils.toCalendar(DATE_2)).test();
-        Convert.fromPrimitive(DATE_1, DATE_2).to(DateUtils.toCalendar(DATE_1), DateUtils.toCalendar(DATE_2)).test();
+        Convert.from(DATE_1, DATE_2).to(toCalendar(DATE_1), toCalendar(DATE_2)).test();
 
         // test other types that should not be converted
         Convert.<String,Calendar>from(STRING_1, STRING_2).toNull(Calendar.class).test();
@@ -331,14 +334,12 @@ public class ObjectConverterTest {
     }
     
     @Test
-    @Ignore // TODO: support date
     public void testToDate() {
         Convert.from(DATE_1, DATE_2).to(DATE_1, DATE_2).test();
-        Convert.from(DATE_1.toString(), DATE_2.toString()).to(DATE_1, DATE_2).test();
+        Convert.from(DateUtils.dateToString(DATE_1), DateUtils.dateToString(DATE_2)).to(DATE_1, DATE_2).test();
         
         // test conversion from other date types
-        Convert.from(CALENDAR_1, CALENDAR_2).to(CALENDAR_1.getTime(), CALENDAR_2.getTime()).test();
-        Convert.fromPrimitive(CALENDAR_1, CALENDAR_2).to(CALENDAR_1.getTime(), CALENDAR_2.getTime()).test();
+        Convert.from(CALENDAR_1, CALENDAR_2).to(toDate(CALENDAR_1), toDate(CALENDAR_2)).test();
 
         // test other types that should not be converted
         Convert.<String,Date>from(STRING_1, STRING_2).toNull(Date.class).test();