You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by th...@apache.org on 2020/12/05 21:54:46 UTC

[tapestry-5] branch master updated: TAP5-2645: Java Time API type coercers (by Ben Weidig)

This is an automated email from the ASF dual-hosted git repository.

thiagohp pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/tapestry-5.git


The following commit(s) were added to refs/heads/master by this push:
     new 0d7f097  TAP5-2645: Java Time API type coercers (by Ben Weidig)
0d7f097 is described below

commit 0d7f097a4a5197228314419807c45a72130c3ae3
Author: Thiago H. de Paula Figueiredo <th...@arsmachina.com.br>
AuthorDate: Sat Dec 5 18:54:26 2020 -0300

    TAP5-2645: Java Time API type coercers (by Ben Weidig)
---
 .../beanmodel/BeanModelSourceBuilder.java          |   1 +
 .../commons/internal/BasicTypeCoercions.java       | 144 +++++++++++++++++++++
 .../test/groovy/ioc/specs/TypeCoercerSpec.groovy   |  79 ++++++++++-
 3 files changed, 223 insertions(+), 1 deletion(-)

diff --git a/beanmodel/src/main/java/org/apache/tapestry5/beanmodel/BeanModelSourceBuilder.java b/beanmodel/src/main/java/org/apache/tapestry5/beanmodel/BeanModelSourceBuilder.java
index 1691f99..92a54e5 100644
--- a/beanmodel/src/main/java/org/apache/tapestry5/beanmodel/BeanModelSourceBuilder.java
+++ b/beanmodel/src/main/java/org/apache/tapestry5/beanmodel/BeanModelSourceBuilder.java
@@ -170,6 +170,7 @@ public class BeanModelSourceBuilder {
     {
         CoercionTupleConfiguration configuration = new CoercionTupleConfiguration();
         BasicTypeCoercions.provideBasicTypeCoercions(configuration);
+        BasicTypeCoercions.provideJSR310TypeCoercions(configuration);
         typeCoercer = new TypeCoercerImpl(configuration.getTuples());
     }
 
diff --git a/commons/src/main/java/org/apache/tapestry5/commons/internal/BasicTypeCoercions.java b/commons/src/main/java/org/apache/tapestry5/commons/internal/BasicTypeCoercions.java
index 6e7b1d8..ea5c7ed 100644
--- a/commons/src/main/java/org/apache/tapestry5/commons/internal/BasicTypeCoercions.java
+++ b/commons/src/main/java/org/apache/tapestry5/commons/internal/BasicTypeCoercions.java
@@ -17,15 +17,34 @@ import java.io.File;
 import java.lang.reflect.Array;
 import java.math.BigDecimal;
 import java.math.BigInteger;
+import java.time.DayOfWeek;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Month;
+import java.time.MonthDay;
+import java.time.OffsetDateTime;
+import java.time.OffsetTime;
+import java.time.Period;
+import java.time.Year;
+import java.time.YearMonth;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 
+import org.apache.tapestry5.commons.Configuration;
 import org.apache.tapestry5.commons.MappedConfiguration;
 import org.apache.tapestry5.commons.services.Coercion;
 import org.apache.tapestry5.commons.services.CoercionTuple;
 import org.apache.tapestry5.commons.services.TypeCoercer;
+import org.apache.tapestry5.commons.util.StringToEnumCoercion;
 import org.apache.tapestry5.commons.util.TimeInterval;
 import org.apache.tapestry5.func.Flow;
 
@@ -108,6 +127,8 @@ public class BasicTypeCoercions
             }
         });
 
+        add(configuration, String.class, Integer.class, Integer::valueOf);
+
         add(configuration, Long.class, Byte.class, new Coercion<Long, Byte>()
         {
             @Override
@@ -320,6 +341,129 @@ public class BasicTypeCoercions
 
     }
 
+    /**
+     * Provides the basic type coercions for JSR310 (java.time.*) to a {@link Configuration}
+     * instance.
+     * TAP5-2645
+     */
+    public static void provideJSR310TypeCoercions(
+            MappedConfiguration<CoercionTuple.Key, CoercionTuple> configuration)
+    {
+        {
+            add(configuration, Year.class, Integer.class, Year::getValue);
+            add(configuration, Integer.class, Year.class, Year::of);
+        }
+
+        {
+            add(configuration, Month.class, Integer.class, Month::getValue);
+            add(configuration, Integer.class, Month.class, Month::of);
+
+            add(configuration, String.class, Month.class, StringToEnumCoercion.create(Month.class));
+        }
+
+        {
+            add(configuration, String.class, YearMonth.class, YearMonth::parse);
+
+            add(configuration, YearMonth.class, Year.class, input -> Year.of(input.getYear()));
+            add(configuration, YearMonth.class, Month.class, YearMonth::getMonth);
+        }
+
+        {
+            add(configuration, String.class, MonthDay.class, MonthDay::parse);
+
+            add(configuration, MonthDay.class, Month.class, MonthDay::getMonth);
+        }
+
+        {
+            add(configuration, DayOfWeek.class, Integer.class, DayOfWeek::getValue);
+            add(configuration, Integer.class, DayOfWeek.class, DayOfWeek::of);
+
+            add(configuration, String.class, DayOfWeek.class,
+                    StringToEnumCoercion.create(DayOfWeek.class));
+        }
+
+        {
+            add(configuration, LocalDate.class, Instant.class, input -> {
+                return input.atStartOfDay(ZoneId.systemDefault()).toInstant();
+            });
+            add(configuration, Instant.class, LocalDate.class, input -> {
+                return input.atZone(ZoneId.systemDefault()).toLocalDate();
+            });
+
+            add(configuration, String.class, LocalDate.class, LocalDate::parse);
+
+            add(configuration, LocalDate.class, YearMonth.class, input -> {
+                return YearMonth.of(input.getYear(), input.getMonth());
+            });
+
+            add(configuration, LocalDate.class, MonthDay.class, input -> {
+                return MonthDay.of(input.getMonth(), input.getDayOfMonth());
+            });
+        }
+
+        {
+            add(configuration, LocalTime.class, Long.class, LocalTime::toNanoOfDay);
+            add(configuration, Long.class, LocalTime.class, LocalTime::ofNanoOfDay);
+
+            add(configuration, String.class, LocalTime.class, LocalTime::parse);
+        }
+
+        {
+            add(configuration, String.class, LocalDateTime.class, LocalDateTime::parse);
+
+            add(configuration, LocalDateTime.class, Instant.class, input -> {
+                return input.atZone(ZoneId.systemDefault()).toInstant();
+            });
+            add(configuration, Instant.class, LocalDateTime.class, input -> {
+                return LocalDateTime.ofInstant(input, ZoneId.systemDefault());
+            });
+
+            add(configuration, LocalDateTime.class, LocalDate.class, LocalDateTime::toLocalDate);
+        }
+
+        {
+            add(configuration, String.class, OffsetDateTime.class, OffsetDateTime::parse);
+
+            add(configuration, OffsetDateTime.class, Instant.class, OffsetDateTime::toInstant);
+
+            add(configuration, OffsetDateTime.class, OffsetTime.class,
+                    OffsetDateTime::toOffsetTime);
+        }
+
+        {
+            add(configuration, String.class, ZoneId.class, ZoneId::of);
+        }
+
+        {
+            add(configuration, String.class, ZoneOffset.class, ZoneOffset::of);
+        }
+
+        {
+            add(configuration, String.class, ZonedDateTime.class, ZonedDateTime::parse);
+
+            add(configuration, ZonedDateTime.class, Instant.class, ZonedDateTime::toInstant);
+
+            add(configuration, ZonedDateTime.class, ZoneId.class, ZonedDateTime::getZone);
+        }
+
+        {
+            add(configuration, Instant.class, Long.class, Instant::toEpochMilli);
+            add(configuration, Long.class, Instant.class, Instant::ofEpochMilli);
+
+            add(configuration, Instant.class, Date.class, Date::from);
+            add(configuration, Date.class, Instant.class, Date::toInstant);
+        }
+
+        {
+            add(configuration, Duration.class, Long.class, Duration::toNanos);
+            add(configuration, Long.class, Duration.class, Duration::ofNanos);
+        }
+
+        {
+            add(configuration, String.class, Period.class, Period::parse);
+        }
+    }
+
     private static <S, T> void add(MappedConfiguration<CoercionTuple.Key, CoercionTuple> configuration, Class<S> sourceType,
                                    Class<T> targetType, Coercion<S, T> coercion)
     {
diff --git a/tapestry-ioc/src/test/groovy/ioc/specs/TypeCoercerSpec.groovy b/tapestry-ioc/src/test/groovy/ioc/specs/TypeCoercerSpec.groovy
index a804cc0..c9e3583 100644
--- a/tapestry-ioc/src/test/groovy/ioc/specs/TypeCoercerSpec.groovy
+++ b/tapestry-ioc/src/test/groovy/ioc/specs/TypeCoercerSpec.groovy
@@ -1,5 +1,22 @@
 package ioc.specs
 
+import java.time.DayOfWeek
+import java.time.Duration
+import java.time.Instant
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.LocalTime
+import java.time.Month
+import java.time.MonthDay
+import java.time.OffsetDateTime
+import java.time.OffsetTime
+import java.time.Period
+import java.time.Year
+import java.time.YearMonth
+import java.time.ZoneId
+import java.time.ZoneOffset
+import java.time.ZonedDateTime
+
 import org.apache.tapestry5.commons.services.TypeCoercer
 import org.apache.tapestry5.commons.util.TimeInterval
 import org.apache.tapestry5.func.F
@@ -104,6 +121,66 @@ class TypeCoercerSpec extends AbstractSharedRegistrySpecification {
     Animal.DOG                        | String               | 'DOG'
     'CAT'                             | Animal               | Animal.CAT
 
+    // TAP5-2645
+    2020                              | Year                 | Year.of(2020)
+    Year.of(2020)                     | Integer              | 2020
+    2020                              | Year                 | Year.of(2020)
+    Month.OCTOBER                     | Integer              | 10
+    10                                | Month                | Month.OCTOBER
+    Month.OCTOBER                     | String               | "OCTOBER"
+    "oCTobER"                         | Month                | Month.OCTOBER
+    "--10-04"                         | MonthDay             | MonthDay.of(10, 4)
+    MonthDay.of(10, 4)                | String               | "--10-04"
+    "2020-10"                         | YearMonth            | YearMonth.of(2020, 10)
+    YearMonth.of(2020, 10)            | String               | "2020-10"
+    "2020-10-04"                      | LocalDate            | LocalDate.of(2020, 10, 4)
+    LocalDate.of(2020, 10, 4)         | Long                 | ZonedDateTime.of(LocalDate.of(2020, 10, 4), LocalTime.of(0, 0), ZoneId.systemDefault()).toEpochSecond() * 1_000
+    LocalDate.of(2020, 10, 4)         | String               | "2020-10-04"
+    LocalDate.of(2020, 10, 4)         | YearMonth            | YearMonth.of(2020, 10)
+    LocalDate.of(2020, 10, 4)         | MonthDay             | MonthDay.of( 10, 4)
+    YearMonth.of(2020, 10)            | Month                | Month.OCTOBER
+    YearMonth.of(2020, 10)            | Year                 | Year.of(2020)
+    DayOfWeek.THURSDAY                | Integer              | 4
+    4                                 | DayOfWeek            | DayOfWeek.THURSDAY
+    DayOfWeek.THURSDAY                | String               | "THURSDAY"
+    "THURSdaY"                        | DayOfWeek            | DayOfWeek.THURSDAY
+    LocalDate.of(2020, 10, 4)         | String               | "2020-10-04"
+    "2020-10-04"                      | LocalDate            | LocalDate.of(2020, 10, 4)
+    34862776991000L                   | LocalTime            | LocalTime.of(9, 41, 2, 776991000)
+    LocalTime.of(9, 41, 2, 776991000) | Long                 | 34862776991000L
+    "09:41:02.776991"                 | LocalTime            | LocalTime.of(9, 41, 2, 776991000)
+    LocalTime.of(9, 41, 2, 776991000) | String               | "09:41:02.776991"
+
+    "2020-10-04T16:59:51.713909"                         | LocalDateTime  | LocalDateTime.of(2020, 10, 4, 16, 59, 51, 713909000)
+    LocalDateTime.of(2020, 10, 4, 16, 59, 51, 713909000) | String         | "2020-10-04T16:59:51.713909"
+    LocalDateTime.of(2020, 10, 4, 16, 59, 51, 713909000) | LocalDate      | LocalDate.of(2020, 10, 4)
+    LocalDateTime.of(2020, 10, 4, 16, 59, 51, 0)         | Long           | ZonedDateTime.of(LocalDate.of(2020, 10, 4), LocalTime.of(16, 59, 51, 0), ZoneId.systemDefault()).toEpochSecond() * 1_000
+
+
+    "2020-10-04T16:59:51.713909+02:00"                                           | OffsetDateTime | OffsetDateTime.of(2020, 10, 4, 16, 59, 51, 713909000, ZoneOffset.ofHours(2))
+    OffsetDateTime.of(2020, 10, 4, 16, 59, 51, 713909000, ZoneOffset.ofHours(2)) | String         | "2020-10-04T16:59:51.713909+02:00"
+    OffsetDateTime.of(2020, 10, 4, 16, 59, 51, 713909000, ZoneOffset.ofHours(2)) | OffsetTime     | OffsetTime.of(16, 59, 51, 713909000, ZoneOffset.ofHours(2))
+
+    "Europe/Berlin"                     | ZoneId     | ZoneId.of("Europe/Berlin")
+    ZoneId.of("Europe/Berlin")          | String     | "Europe/Berlin"
+    "+02:00"                            | ZoneOffset | ZoneOffset.ofHours(2)
+    ZoneOffset.ofHoursMinutes(-2, -30) | String     | "-02:30"
+
+    "2020-10-04T16:59:51.713909+02:00[Europe/Berlin]"                                | ZonedDateTime | ZonedDateTime.of(2020, 10, 4, 16, 59, 51, 713909000, ZoneId.of("Europe/Berlin"))
+    ZonedDateTime.of(2020, 10, 4, 16, 59, 51, 713909000, ZoneId.of("Europe/Berlin")) | String        | "2020-10-04T16:59:51.713909+02:00[Europe/Berlin]"
+    ZonedDateTime.of(2020, 10, 4, 16, 59, 51, 713909000, ZoneId.of("Europe/Berlin")) | ZoneId        | ZoneId.of("Europe/Berlin")
+
+    -1234L                            | Instant              | Instant.ofEpochMilli(-1234L)
+    Instant.ofEpochMilli(-1234L)      | Long                 | -1234L
+    new Date(1606653132000L)          | Instant              | Instant.ofEpochMilli(1606653132000L)
+    Instant.ofEpochMilli(-1234L)      | Long                 | -1234L
+    97200000000000L                   | Duration             | Duration.ofHours(27L)
+    Duration.ofMinutes(90L)           | Long                 | 5400000000000L
+    Period.of(12, 1, 7)               | String               | "P12Y1M7D"
+    "P12Y1M7D"                        | Period               | Period.of(12, 1, 7)
+
+    ZonedDateTime.of(LocalDate.of(2020, 11, 29), LocalTime.of(13, 32, 12, 0), ZoneId.of("Europe/Berlin")).toEpochSecond() * 1_000 | Date | new Date(1606653132000L)
+    
     inputTypeName = PlasticUtils.toTypeName(input.getClass())
     typeName = PlasticUtils.toTypeName(type)
   }
@@ -118,7 +195,7 @@ class TypeCoercerSpec extends AbstractSharedRegistrySpecification {
     where:
 
     from         | to         | expected
-    StringBuffer | Integer    | "Object --> String, String --> Long, Long --> Integer"
+    StringBuffer | Integer    | "Object --> String, String --> Integer"
     void         | Map        | "null --> null"
     void         | Boolean    | "null --> Boolean"
     Object[]     | Boolean    | "Object[] --> Boolean"