You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by rw...@apache.org on 2017/10/23 18:02:33 UTC

svn commit: r1813048 - in /pivot/trunk/core: src/org/apache/pivot/util/CalendarDate.java src/org/apache/pivot/util/Time.java test/org/apache/pivot/util/test/CalendarDateTest.java test/org/apache/pivot/util/test/TimeTest.java

Author: rwhitcomb
Date: Mon Oct 23 18:02:33 2017
New Revision: 1813048

URL: http://svn.apache.org/viewvc?rev=1813048&view=rev
Log:
PIVOT-1010: Add LocalDate and LocalTime support to our CalendarDate and Time
classes to facilitate interaction with the new Java 8 time classes.

Also make some other changes:
* Add a CalendarDateTest class to test these things.
* Beef up the TimeTest class to actually do some asserts, and to test the
  LocalTime conversions.
* Add more supported formats in CalendarDate.Range and Time.Range "decode"
  methods: JSON array/list format, and simple CSV list (similar to other
  changes to Bounds, Span, etc.)
* Add "equals" and "hashCode" methods to these Range classes.
* Use Utils methods to check parameters.

Added:
    pivot/trunk/core/test/org/apache/pivot/util/test/CalendarDateTest.java
Modified:
    pivot/trunk/core/src/org/apache/pivot/util/CalendarDate.java
    pivot/trunk/core/src/org/apache/pivot/util/Time.java
    pivot/trunk/core/test/org/apache/pivot/util/test/TimeTest.java

Modified: pivot/trunk/core/src/org/apache/pivot/util/CalendarDate.java
URL: http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/util/CalendarDate.java?rev=1813048&r1=1813047&r2=1813048&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/util/CalendarDate.java (original)
+++ pivot/trunk/core/src/org/apache/pivot/util/CalendarDate.java Mon Oct 23 18:02:33 2017
@@ -18,12 +18,16 @@ package org.apache.pivot.util;
 
 import java.io.Serializable;
 import java.text.NumberFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.pivot.collections.Dictionary;
+import org.apache.pivot.collections.List;
 import org.apache.pivot.json.JSONSerializer;
 import org.apache.pivot.serialization.SerializationException;
 
@@ -54,24 +58,24 @@ public final class CalendarDate implemen
             this.end = end;
         }
 
+        public Range(String date) {
+            this.start = this.end = CalendarDate.decode(date);
+        }
+
         public Range(String start, String end) {
             this.start = CalendarDate.decode(start);
             this.end = CalendarDate.decode(end);
         }
 
         public Range(Range range) {
-            if (range == null) {
-                throw new IllegalArgumentException("range is null.");
-            }
+            Utils.checkNull(range, "range");
 
             this.start = range.start;
             this.end = range.end;
         }
 
         public Range(Dictionary<String, ?> range) {
-            if (range == null) {
-                throw new IllegalArgumentException("range is null.");
-            }
+            Utils.checkNull(range, "range");
 
             Object startRange = range.get(START_KEY);
             Object endRange = range.get(END_KEY);
@@ -102,9 +106,7 @@ public final class CalendarDate implemen
         }
 
         public boolean contains(Range range) {
-            if (range == null) {
-                throw new IllegalArgumentException("range is null.");
-            }
+            Utils.checkNull(range, "range");
 
             Range normalizedRange = range.normalize();
 
@@ -119,9 +121,7 @@ public final class CalendarDate implemen
         }
 
         public boolean contains(CalendarDate calendarDate) {
-            if (calendarDate == null) {
-                throw new IllegalArgumentException("calendarDate is null.");
-            }
+            Utils.checkNull(calendarDate, "calendarDate");
 
             boolean contains;
             if (this.start.compareTo(this.end) < 0) {
@@ -134,9 +134,7 @@ public final class CalendarDate implemen
         }
 
         public boolean intersects(Range range) {
-            if (range == null) {
-                throw new IllegalArgumentException("range is null.");
-            }
+            Utils.checkNull(range, "range");
 
             Range normalizedRange = range.normalize();
 
@@ -156,10 +154,24 @@ public final class CalendarDate implemen
             return new Range(earlier, later);
         }
 
+        @Override
+        public boolean equals(Object o) {
+            if (o != null && o instanceof Range) {
+                Range r = (Range)o;
+                return r.start.equals(this.start) &&
+                    r.end.equals(this.end);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            // TODO: is this is a good calculation?
+            return start.hashCode() * end.hashCode();
+        }
+
         public static Range decode(String value) {
-            if (value == null) {
-                throw new IllegalArgumentException();
-            }
+            Utils.checkNull(value, "value");
 
             Range range;
             if (value.startsWith("{")) {
@@ -168,8 +180,23 @@ public final class CalendarDate implemen
                 } catch (SerializationException exception) {
                     throw new IllegalArgumentException(exception);
                 }
+            } else if (value.startsWith("[")) {
+                try {
+                    @SuppressWarnings("unchecked")
+                    List<String> values = (List<String>)JSONSerializer.parseList(value);
+                    range = new Range(values.get(0), values.get(1));
+                } catch (SerializationException exception) {
+                    throw new IllegalArgumentException(exception);
+                }
             } else {
-                range = new Range(CalendarDate.decode(value));
+                String[] parts = value.split("\\s*[,;]\\s*");
+                if (parts.length == 2) {
+                    range = new Range(parts[0], parts[1]);
+                } else if (parts.length == 1) {
+                    range = new Range(value);
+                } else {
+                    throw new IllegalArgumentException("Invalid format for Range: " + value);
+                }
             }
 
             return range;
@@ -212,8 +239,22 @@ public final class CalendarDate implemen
      * @param calendar The calendar containing the year, month, and day fields.
      */
     public CalendarDate(GregorianCalendar calendar) {
-        this(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
-            calendar.get(Calendar.DAY_OF_MONTH) - 1);
+        this(calendar.get(Calendar.YEAR),
+             calendar.get(Calendar.MONTH),
+             calendar.get(Calendar.DAY_OF_MONTH) - 1);
+    }
+
+    /**
+     * Creates a new <tt>CalendarDate</tt> from the given {@link LocalDate}
+     * (new in Java 8).  This does not represent a moment in time, but only
+     * represents a date (as in year, month and day).
+     *
+     * @param localDate The date value containing year, month and day fields.
+     */
+    public CalendarDate(LocalDate localDate) {
+        this(localDate.getYear(),
+             localDate.getMonthValue() - 1,
+             localDate.getDayOfMonth() - 1);
     }
 
     /**
@@ -358,6 +399,26 @@ public final class CalendarDate implemen
     }
 
     /**
+     * @return An equivalent {@link LocalDate} that represents the same calendar date
+     * as this date.
+     */
+    public LocalDate toLocalDate() {
+        return LocalDate.of(this.year, this.month + 1, this.day + 1);
+    }
+
+    /**
+     * @return A date and time representing this calendar date along with the given
+     * wall clock time.
+     *
+     * @param time The wall clock time to combine with this calendar date.
+     */
+    public LocalDateTime toLocalDateTime(Time time) {
+        LocalTime localTime = LocalTime.of(time.hour, time.minute, time.second, time.millisecond * 1_000_000);
+        LocalDate localDate = toLocalDate();
+        return localDate.atTime(localTime);
+    }
+
+    /**
      * Compares this calendar date with another calendar date.
      *
      * @param calendarDate The calendar date against which to compare.

Modified: pivot/trunk/core/src/org/apache/pivot/util/Time.java
URL: http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/util/Time.java?rev=1813048&r1=1813047&r2=1813048&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/util/Time.java (original)
+++ pivot/trunk/core/src/org/apache/pivot/util/Time.java Mon Oct 23 18:02:33 2017
@@ -18,14 +18,17 @@ package org.apache.pivot.util;
 
 import java.io.Serializable;
 import java.text.NumberFormat;
+import java.time.LocalTime;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.pivot.collections.Dictionary;
+import org.apache.pivot.collections.List;
 import org.apache.pivot.json.JSONSerializer;
 import org.apache.pivot.serialization.SerializationException;
+import org.apache.pivot.util.Utils;
 
 /**
  * Class representing a time of day, independent of any particular time zone.
@@ -52,24 +55,24 @@ public final class Time implements Compa
             this.end = end;
         }
 
+        public Range(String time) {
+            this.start = this.end = Time.decode(time);
+        }
+
         public Range(String start, String end) {
             this.start = Time.decode(start);
             this.end = Time.decode(end);
         }
 
         public Range(Range range) {
-            if (range == null) {
-                throw new IllegalArgumentException("range is null.");
-            }
+            Utils.checkNull(range, "range");
 
             this.start = range.start;
             this.end = range.end;
         }
 
         public Range(Dictionary<String, ?> range) {
-            if (range == null) {
-                throw new IllegalArgumentException("range is null.");
-            }
+            Utils.checkNull(range, "range");
 
             Object startRange = range.get(START_KEY);
             Object endRange = range.get(END_KEY);
@@ -100,9 +103,7 @@ public final class Time implements Compa
         }
 
         public boolean contains(Range range) {
-            if (range == null) {
-                throw new IllegalArgumentException("range is null.");
-            }
+            Utils.checkNull(range, "range");
 
             Range normalizedRange = range.normalize();
 
@@ -117,9 +118,7 @@ public final class Time implements Compa
         }
 
         public boolean contains(Time time) {
-            if (time == null) {
-                throw new IllegalArgumentException("time is null.");
-            }
+            Utils.checkNull(time, "time");
 
             boolean contains;
             if (this.start.compareTo(this.end) < 0) {
@@ -132,9 +131,7 @@ public final class Time implements Compa
         }
 
         public boolean intersects(Range range) {
-            if (range == null) {
-                throw new IllegalArgumentException("range is null.");
-            }
+            Utils.checkNull(range, "range");
 
             Range normalizedRange = range.normalize();
 
@@ -154,10 +151,24 @@ public final class Time implements Compa
             return new Range(earlier, later);
         }
 
+        @Override
+        public boolean equals(Object o) {
+            if (o != null && o instanceof Range) {
+                Range r = (Range)o;
+                return r.start.equals(this.start) &&
+                    r.end.equals(this.end);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            // TODO: is this is a good calculation?
+            return start.hashCode() * end.hashCode();
+        }
+
         public static Range decode(String value) {
-            if (value == null) {
-                throw new IllegalArgumentException();
-            }
+            Utils.checkNull(value, "value");
 
             Range range;
             if (value.startsWith("{")) {
@@ -166,8 +177,23 @@ public final class Time implements Compa
                 } catch (SerializationException exception) {
                     throw new IllegalArgumentException(exception);
                 }
+            } else if (value.startsWith("[")) {
+                try {
+                    @SuppressWarnings("unchecked")
+                    List<String> values = (List<String>)JSONSerializer.parseList(value);
+                    range = new Range(values.get(0), values.get(1));
+                } catch (SerializationException exception) {
+                    throw new IllegalArgumentException(exception);
+                }
             } else {
-                range = new Range(Time.decode(value));
+                String[] parts = value.split("\\s*[,;]\\s*");
+                if (parts.length == 2) {
+                    range = new Range(parts[0], parts[1]);
+                } else if (parts.length == 1) {
+                    range = new Range(value);
+                } else {
+                    throw new IllegalArgumentException("Invalid format for Range: " + value);
+                }
             }
 
             return range;
@@ -199,16 +225,16 @@ public final class Time implements Compa
     public static final int MILLISECONDS_PER_HOUR = 60 * MILLISECONDS_PER_MINUTE;
     public static final int MILLISECONDS_PER_DAY = 24 * MILLISECONDS_PER_HOUR;
 
-    private static final Pattern PATTERN = Pattern.compile("^(\\d{2}):(\\d{2}):(\\d{2})(\\.(\\d{3}))?$");
+    public static final int NANOS_PER_MILLI = 1_000_000;
+
+    private static final Pattern PATTERN = Pattern.compile("^(\\d{1,2}):(\\d{2}):(\\d{2})(\\.(\\d{3}))?$");
 
     public Time() {
         this(new GregorianCalendar());
     }
 
     public Time(Calendar calendar) {
-        if (calendar == null) {
-            throw new IllegalArgumentException();
-        }
+        Utils.checkNull(calendar, "calendar");
 
         this.hour = calendar.get(Calendar.HOUR_OF_DAY);
         this.minute = calendar.get(Calendar.MINUTE);
@@ -220,22 +246,17 @@ public final class Time implements Compa
         this(hour, minute, second, 0);
     }
 
-    public Time(int hour, int minute, int second, int millisecond) {
-        if (hour < 0 || hour > 23) {
-            throw new IllegalArgumentException("Invalid hour.");
-        }
-
-        if (minute < 0 || minute > 59) {
-            throw new IllegalArgumentException("Invalid minute.");
-        }
-
-        if (second < 0 || second > 59) {
-            throw new IllegalArgumentException("Invalid second.");
+    private void check(int value, int limit, String part) {
+        if (value < 0 || value > limit) {
+            throw new IllegalArgumentException("Invalid " + part + ".");
         }
+    }
 
-        if (millisecond < 0 || millisecond > 999) {
-            throw new IllegalArgumentException("Invalid millisecond.");
-        }
+    public Time(int hour, int minute, int second, int millisecond) {
+        check(hour, 23, "hour");
+        check(minute, 59, "minute");
+        check(second, 59, "second");
+        check(millisecond, 999, "millisecond");
 
         this.hour = hour;
         this.minute = minute;
@@ -262,6 +283,20 @@ public final class Time implements Compa
     }
 
     /**
+     * Construct a <tt>Time</tt> from a {@link LocalTime}, rounding
+     * up the nanosecond value to our milliseconds.
+     *
+     * @param localTime The local time to convert.
+     * @see #NANOS_PER_MILLI
+     */
+    public Time(LocalTime localTime) {
+        this(localTime.getHour(),
+             localTime.getMinute(),
+             localTime.getSecond(),
+             ((localTime.getNano() + (NANOS_PER_MILLI / 2)) / NANOS_PER_MILLI));
+    }
+
+    /**
      * Adds the specified milliseconds of days to this time and returns the
      * resulting time. The number of milliseconds may be negative, in which case
      * the result will be a time prior to this time.
@@ -284,25 +319,26 @@ public final class Time implements Compa
      * @return The number of milliseconds in between this time and <tt>time</tt>.
      */
     public int subtract(Time time) {
-        if (time == null) {
-            throw new IllegalArgumentException();
-        }
+        Utils.checkNull(time, "time");
 
         return toMilliseconds() - time.toMilliseconds();
     }
 
     /**
-     * Returns the number of milliseconds since midnight represented by this
-     * time.
-     *
-     * @return The number of milliseconds since midnight represented by this
-     * time.
+     * @return The number of milliseconds since midnight represented by this time.
      */
     public int toMilliseconds() {
         return this.hour * MILLISECONDS_PER_HOUR + this.minute * MILLISECONDS_PER_MINUTE
             + this.second * MILLISECONDS_PER_SECOND + this.millisecond;
     }
 
+    /**
+     * @return This time converted to a {@link LocalTime}.
+     */
+    public LocalTime toLocalTime() {
+        return LocalTime.of(this.hour, this.minute, this.second, this.millisecond * NANOS_PER_MILLI);
+    }
+
     @Override
     public int compareTo(Time time) {
         int result = this.hour - time.hour;

Added: pivot/trunk/core/test/org/apache/pivot/util/test/CalendarDateTest.java
URL: http://svn.apache.org/viewvc/pivot/trunk/core/test/org/apache/pivot/util/test/CalendarDateTest.java?rev=1813048&view=auto
==============================================================================
--- pivot/trunk/core/test/org/apache/pivot/util/test/CalendarDateTest.java (added)
+++ pivot/trunk/core/test/org/apache/pivot/util/test/CalendarDateTest.java Mon Oct 23 18:02:33 2017
@@ -0,0 +1,82 @@
+/*
+ * 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.pivot.util.test;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import org.apache.pivot.util.CalendarDate;
+import org.apache.pivot.util.Time;
+
+
+public class CalendarDateTest {
+    private static final String d1 = "1941-12-07";
+    private static final String d2 = "1929-10-29";
+    private static final String d3 = "2008-09-29";
+
+    @Test
+    public void test1() {
+        CalendarDate.Range r1 = new CalendarDate.Range(d1);
+        CalendarDate.Range r1a = new CalendarDate.Range(d1, d1);
+        CalendarDate.Range r2 = new CalendarDate.Range(d2, d3);
+        CalendarDate.Range r3 = CalendarDate.Range.decode("{ \"start\" : \"1929-10-29\", \"end\" : \"2008-09-29\"}");
+        CalendarDate.Range r3a = CalendarDate.Range.decode("[ \"1929-10-29\", \"2008-09-29\" ]");
+        CalendarDate.Range r3b = CalendarDate.Range.decode("1929-10-29, 2008-09-29");
+
+        CalendarDate cd1 = CalendarDate.decode(d1);
+        CalendarDate cd2 = CalendarDate.decode(d2);
+        CalendarDate cd3 = CalendarDate.decode(d3);
+
+        assertTrue(r2.contains(r1));
+        assertEquals(r1, r1a);
+        assertEquals(r1.getLength(), 1);
+        assertTrue(r2.normalize().equals(r2));
+        // TODO: more tests of range methods: intersects, etc.
+
+        assertEquals(r3, r3a);
+        assertEquals(r3, r3b);
+        assertEquals(r3a, r3b);
+
+        assertEquals(cd1.year, 1941);
+        assertEquals(cd1.month, 11);
+        assertEquals(cd1.day, 6);
+        assertEquals(cd1.toString(), d1);
+    }
+
+    @Test
+    public void test2() {
+        // PIVOT-1010: test interaction with LocalDate, etc. (new Java 8 classes)
+        LocalDate ld1 = LocalDate.of(1941, 12, 7);
+        CalendarDate cd1 = new CalendarDate(ld1);
+        CalendarDate cd1a = CalendarDate.decode(d1);
+        LocalDate ld1a = cd1a.toLocalDate();
+
+        assertEquals(cd1, cd1a);
+        assertEquals(ld1, ld1a);
+
+        Time t1 = Time.decode("07:48:00");
+        LocalDateTime dt1 = LocalDateTime.of(1941, 12, 7, 7, 48, 0);
+        LocalDateTime dt1a = cd1.toLocalDateTime(t1);
+
+        assertEquals(dt1, dt1a);
+    }
+}

Modified: pivot/trunk/core/test/org/apache/pivot/util/test/TimeTest.java
URL: http://svn.apache.org/viewvc/pivot/trunk/core/test/org/apache/pivot/util/test/TimeTest.java?rev=1813048&r1=1813047&r2=1813048&view=diff
==============================================================================
--- pivot/trunk/core/test/org/apache/pivot/util/test/TimeTest.java (original)
+++ pivot/trunk/core/test/org/apache/pivot/util/test/TimeTest.java Mon Oct 23 18:02:33 2017
@@ -16,8 +16,11 @@
  */
 package org.apache.pivot.util.test;
 
+import java.time.LocalTime;
+
 import org.apache.pivot.util.Time;
 import org.junit.Test;
+import static org.junit.Assert.assertEquals;
 
 public class TimeTest {
     @Test
@@ -32,33 +35,87 @@ public class TimeTest {
         System.out.println(time);
 
         time = new Time(0, 0, 0);
-        System.out.println(time.subtract(new Time(0, 0, 1)));
-        System.out.println(time.subtract(new Time(23, 59, 59, 999)));
-
-        time = new Time(0, 0, 0);
-        System.out.println(time.add(1));
-        System.out.println(time.add(Time.MILLISECONDS_PER_DAY + 1));
-        System.out.println(time.add(-1));
-        System.out.println(time.add(-Time.MILLISECONDS_PER_DAY - 1));
-        System.out.println(time.add(1000));
-        System.out.println(time.add(-1000));
+        int i1 = time.subtract(new Time(0, 0, 1));
+        System.out.println(i1);
+        assertEquals(i1, -1000);
+
+        int i2 = time.subtract(new Time(23, 59, 59, 999));
+        System.out.println(i2);
+        assertEquals(i2, -(Time.MILLISECONDS_PER_DAY - 1));
+
+        Time time0 = new Time(0, 0, 0);
+        time = time0;
+        Time time1 = time.add(1);
+        System.out.println(time1);
+        assertEquals(time1.toString(), "00:00:00.001");
+
+        Time time2 = time.add(Time.MILLISECONDS_PER_DAY + 1);
+        System.out.println(time2);
+        assertEquals(time1, time2);
+        assertEquals(time2.toString(), "00:00:00.001");
+
+        Time time3 = time.add(-1);
+        System.out.println(time3);
+        assertEquals(time3.toString(), "23:59:59.999");
+
+        Time time4 = time.add(-Time.MILLISECONDS_PER_DAY - 1);
+        System.out.println(time4);
+        assertEquals(time4.toString(), "23:59:59.999");
+
+        Time time5 = time.add(1000);
+        Time time5a = new Time(0, 0, 1);
+        System.out.println(time5);
+        assertEquals(time5, time5a);
+        assertEquals(time5.toString(), "00:00:01");
+
+        Time time6 = time.add(-1000);
+        Time time6a = new Time(23, 59, 59);
+        System.out.println(time6);
+        assertEquals(time6, time6a);
+        assertEquals(time6.toString(), "23:59:59");
 
         time = Time.decode("00:00:00");
         System.out.println(time);
+        assertEquals(time, time0);
 
         time = Time.decode("00:00:00.000");
         System.out.println(time);
+        assertEquals(time, time0);
 
         try {
             time = Time.decode("00:00");
         } catch (IllegalArgumentException exception) {
             System.out.println(exception);
+            assertEquals(exception.toString(), "java.lang.IllegalArgumentException: Invalid time format: 00:00");
         }
 
         try {
             time = Time.decode("00:00:00.00");
         } catch (IllegalArgumentException exception) {
             System.out.println(exception);
+            assertEquals(exception.toString(), "java.lang.IllegalArgumentException: Invalid time format: 00:00:00.00");
         }
     }
+
+    @Test
+    public void localTimeTest() {
+        LocalTime localTime = LocalTime.of(7, 48);
+        Time t1 = new Time(localTime);
+        Time t1a = Time.decode("07:48:00.000");
+
+        assertEquals(t1, t1a);
+        assertEquals(t1.toString(), "07:48:00");
+
+        // Test rounding of nanoseconds to milliseconds
+        LocalTime lt1 = LocalTime.of(7, 48, 10, 499999);
+        LocalTime lt2 = LocalTime.of(7, 48, 10, 500000);
+        Time t2 = new Time(lt1);
+        Time t2a = new Time(7, 48, 10, 0);
+        Time t3 = new Time(lt2);
+        Time t3a = new Time(7, 48, 10, 1);
+        assertEquals(t2, t2a);
+        assertEquals(t3, t3a);
+        assertEquals(t2.toString(), "07:48:10");
+        assertEquals(t3.toString(), "07:48:10.001");
+    }
 }