From 555cb8f3ffafd0535274160ca19a820017234839 Mon Sep 17 00:00:00 2001 From: Jakob Vad Nielsen Date: Fri, 14 Jul 2017 16:50:20 +0200 Subject: [PATCH 1/2] Added feature suggestions from Habib Pleines to PR --- .../java8/time/AbstractChecker.java | 22 ++++++++++++++ .../freemarker/java8/time/DateTimeTools.java | 3 ++ .../java8/time/LocalDateAdapter.java | 29 +++++++++++++++++-- .../java8/time/LocalDateTimeAdapter.java | 29 +++++++++++++++++-- .../java8/time/LocalTimeAdapter.java | 29 +++++++++++++++++-- 5 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 src/main/java/no/api/freemarker/java8/time/AbstractChecker.java diff --git a/src/main/java/no/api/freemarker/java8/time/AbstractChecker.java b/src/main/java/no/api/freemarker/java8/time/AbstractChecker.java new file mode 100644 index 0000000..7b46aa6 --- /dev/null +++ b/src/main/java/no/api/freemarker/java8/time/AbstractChecker.java @@ -0,0 +1,22 @@ +package no.api.freemarker.java8.time; + +/** + * Abstract checker class. + * + * Adapters supporting checkers will extend this class. + * + * @param + * The java.time class this formatter handles. + */ +public abstract class AbstractChecker { + + private E obj; + + public AbstractChecker(E obj) { + this.obj = obj; + } + + public E getObject() { + return obj; + } +} \ No newline at end of file diff --git a/src/main/java/no/api/freemarker/java8/time/DateTimeTools.java b/src/main/java/no/api/freemarker/java8/time/DateTimeTools.java index f8aa0b0..1991806 100644 --- a/src/main/java/no/api/freemarker/java8/time/DateTimeTools.java +++ b/src/main/java/no/api/freemarker/java8/time/DateTimeTools.java @@ -31,6 +31,9 @@ */ public final class DateTimeTools { + public static final String METHOD_EQUALS = "isEqual"; + public static final String METHOD_BEFORE = "isBefore"; + public static final String METHOD_AFTER = "isAfter"; public static final String METHOD_FORMAT = "format"; public static final String METHOD_DAYS = "days"; public static final String METHOD_MONTHS = "months"; diff --git a/src/main/java/no/api/freemarker/java8/time/LocalDateAdapter.java b/src/main/java/no/api/freemarker/java8/time/LocalDateAdapter.java index f8de872..4cc5a82 100644 --- a/src/main/java/no/api/freemarker/java8/time/LocalDateAdapter.java +++ b/src/main/java/no/api/freemarker/java8/time/LocalDateAdapter.java @@ -27,9 +27,7 @@ import java.time.format.DateTimeFormatter; import java.util.List; -import static no.api.freemarker.java8.time.DateTimeTools.METHOD_FORMAT; -import static no.api.freemarker.java8.time.DateTimeTools.METHOD_UNKNOWN_MSG; -import static no.api.freemarker.java8.time.DateTimeTools.createDateTimeFormatter; +import static no.api.freemarker.java8.time.DateTimeTools.*; /** * LocalDateAdapter adds basic format support for {@link LocalDate} too FreeMarker 2.3.23 and above. @@ -45,6 +43,8 @@ public LocalDateAdapter(LocalDate obj) { public TemplateModel get(String s) throws TemplateModelException { if (METHOD_FORMAT.equals(s)) { return new LocalDateFormatter(getObject()); + } else if(METHOD_EQUALS.equals(s) || METHOD_AFTER.equals(s) || METHOD_BEFORE.equals(s)) { + return new LocalDateChecker(getObject(), s); } throw new TemplateModelException(METHOD_UNKNOWN_MSG + s); } @@ -60,4 +60,27 @@ public Object exec(List list) throws TemplateModelException { return getObject().format(createDateTimeFormatter(list, 0, DateTimeFormatter.ISO_LOCAL_DATE)); } } + + public class LocalDateChecker extends AbstractChecker implements TemplateMethodModelEx { + private String method; + + public LocalDateChecker(LocalDate obj, String method) { + super(obj); + this.method = method; + } + + @Override + public Object exec(List list) throws TemplateModelException { + LocalDateAdapter adapter = (LocalDateAdapter) list.get(0); + switch(method) { + case METHOD_EQUALS: + return getObject().isEqual(adapter.getObject()); + case METHOD_AFTER: + return getObject().isAfter(adapter.getObject()); + case METHOD_BEFORE: + return getObject().isBefore(adapter.getObject()); + } + throw new TemplateModelException("method not implemented"); + } + } } diff --git a/src/main/java/no/api/freemarker/java8/time/LocalDateTimeAdapter.java b/src/main/java/no/api/freemarker/java8/time/LocalDateTimeAdapter.java index 1dc2165..31ed337 100644 --- a/src/main/java/no/api/freemarker/java8/time/LocalDateTimeAdapter.java +++ b/src/main/java/no/api/freemarker/java8/time/LocalDateTimeAdapter.java @@ -27,9 +27,7 @@ import java.time.format.DateTimeFormatter; import java.util.List; -import static no.api.freemarker.java8.time.DateTimeTools.METHOD_FORMAT; -import static no.api.freemarker.java8.time.DateTimeTools.METHOD_UNKNOWN_MSG; -import static no.api.freemarker.java8.time.DateTimeTools.createDateTimeFormatter; +import static no.api.freemarker.java8.time.DateTimeTools.*; /** * LocalDateTimeAdapter adds basic format support for {@link LocalDateTime} too FreeMarker 2.3.23 and above. @@ -46,6 +44,8 @@ public LocalDateTimeAdapter(LocalDateTime obj) { public TemplateModel get(String s) throws TemplateModelException { if (METHOD_FORMAT.equals(s)) { return new LocalDateTimeFormatter(getObject()); + } else if(METHOD_EQUALS.equals(s) || METHOD_AFTER.equals(s) || METHOD_BEFORE.equals(s)) { + return new LocalDateTimeChecker(getObject(), s); } throw new TemplateModelException(METHOD_UNKNOWN_MSG + s); } @@ -61,4 +61,27 @@ public Object exec(List list) throws TemplateModelException { return getObject().format(createDateTimeFormatter(list, 0, DateTimeFormatter.ISO_ZONED_DATE_TIME)); } } + + public class LocalDateTimeChecker extends AbstractChecker implements TemplateMethodModelEx { + private String method; + + public LocalDateTimeChecker(LocalDateTime obj, String method) { + super(obj); + this.method = method; + } + + @Override + public Object exec(List list) throws TemplateModelException { + LocalDateTimeAdapter adapter = (LocalDateTimeAdapter) list.get(0); + switch(method) { + case METHOD_EQUALS: + return getObject().isEqual(adapter.getObject()); + case METHOD_AFTER: + return getObject().isAfter(adapter.getObject()); + case METHOD_BEFORE: + return getObject().isBefore(adapter.getObject()); + } + throw new TemplateModelException("method not implemented"); + } + } } \ No newline at end of file diff --git a/src/main/java/no/api/freemarker/java8/time/LocalTimeAdapter.java b/src/main/java/no/api/freemarker/java8/time/LocalTimeAdapter.java index fd49bd0..89e7dc4 100644 --- a/src/main/java/no/api/freemarker/java8/time/LocalTimeAdapter.java +++ b/src/main/java/no/api/freemarker/java8/time/LocalTimeAdapter.java @@ -27,9 +27,7 @@ import java.time.format.DateTimeFormatter; import java.util.List; -import static no.api.freemarker.java8.time.DateTimeTools.METHOD_FORMAT; -import static no.api.freemarker.java8.time.DateTimeTools.METHOD_UNKNOWN_MSG; -import static no.api.freemarker.java8.time.DateTimeTools.createDateTimeFormatter; +import static no.api.freemarker.java8.time.DateTimeTools.*; /** * LocalTimeAdapter adds basic format support for {@link LocalTime} too FreeMarker 2.3.23 and above. @@ -45,6 +43,8 @@ public LocalTimeAdapter(LocalTime obj) { public TemplateModel get(String s) throws TemplateModelException { if (METHOD_FORMAT.equals(s)) { return new LocalTimeFormatter(getObject()); + } else if(METHOD_EQUALS.equals(s) || METHOD_AFTER.equals(s) || METHOD_BEFORE.equals(s)) { + return new LocalTimeChecker(getObject(), s); } throw new TemplateModelException(METHOD_UNKNOWN_MSG + s); } @@ -60,4 +60,27 @@ public Object exec(List list) throws TemplateModelException { return getObject().format(createDateTimeFormatter(list, 0, DateTimeFormatter.ISO_LOCAL_TIME)); } } + + public class LocalTimeChecker extends AbstractChecker implements TemplateMethodModelEx { + private String method; + + public LocalTimeChecker(LocalTime obj, String method) { + super(obj); + this.method = method; + } + + @Override + public Object exec(List list) throws TemplateModelException { + LocalTimeAdapter adapter = (LocalTimeAdapter) list.get(0); + switch(method) { + case METHOD_EQUALS: + return getObject().equals(adapter.getObject()); + case METHOD_AFTER: + return getObject().isAfter(adapter.getObject()); + case METHOD_BEFORE: + return getObject().isBefore(adapter.getObject()); + } + throw new TemplateModelException("method not implemented"); + } + } } From 1809c50e43b0471c9ea331fd576286d301b876ee Mon Sep 17 00:00:00 2001 From: Jakob Vad Nielsen Date: Mon, 24 Jul 2017 16:19:08 +0200 Subject: [PATCH 2/2] Added tests and doc --- CHANGELOG.md | 5 + README.adoc | 22 ++- .../java8/time/LocalDateAdapter.java | 1 + .../java8/time/LocalDateTimeAdapter.java | 1 + .../java8/time/DateTimeStepdefs.java | 36 +++++ .../freemarker/java8/time/comparison.feature | 151 ++++++++++++++++++ 6 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/no/api/freemarker/java8/time/comparison.feature diff --git a/CHANGELOG.md b/CHANGELOG.md index f8ed662..c4a16cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## [freemarker-java8-1.1.6](https://github.com/amedia/freemarker-java-8/tree/freemarker-java8-1.1.6) (2017-07-24) +[Full Changelog](https://github.com/amedia/freemarker-java-8/compare/freemarker-java8-1.1.5...freemarker-java8-1.1.6) + +- Added isEqual, isAfter and isBefore methods to adapters for LocalDate, LocalDateTime and Localtime (Code contributed by @kingmaoam) + ## [freemarker-java8-1.1.5](https://github.com/amedia/freemarker-java-8/tree/freemarker-java8-1.1.5) (2016-12-19) [Full Changelog](https://github.com/amedia/freemarker-java-8/compare/freemarker-java8-1.1.4...freemarker-java8-1.1.5) diff --git a/README.adoc b/README.adoc index 2bfc3e7..687e9f6 100644 --- a/README.adoc +++ b/README.adoc @@ -9,7 +9,7 @@ new https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter This is not a perfect solution as we cannot add built-ins to FreeMarker from outside code. But its the best we can do until FreeMarker adds full support in the future. -Out Cucumber feature test spec will hopefully explain how it all works : https://github.com/amedia/freemarker-java-8/blob/master/src/test/resources/no/api/freemarker/java8/time/datetime.feature +Out Cucumber feature test spec will hopefully explain how it all works : https://github.com/amedia/freemarker-java-8/blob/master/src/test/resources/no/api/freemarker/java8/time/ ## Requirements @@ -131,3 +131,23 @@ for formatting. |Prints the ZoneOffset display name. You can override the textstyle with one of these values [FULL, FULL_STANDALONE, SHORT, SHORT_STANDALONE, NARROW and NARROW_STANDALONE]. You can also override the locale, but Java only seems to have locale support for a few languages. |${myzoneoffset.format()}` or `${myzoneoffset.format('short')}` or `${myzoneoffset.format('short', 'no-NO')} |=== + +[cols="^,^,^,^", options="header"] +.java.time comparison methods +|=== +| java.time class | methods | comment | example + +|LocalDate +|`isEqual(), isAfter(), isBefore()` +|Can compare two LocalDate objects for equality. +|`${localDate.isEqual(anotherlocalDate)} or ${localDate.isAfter(anotherlocalDate)} or ${localDate.isBefore(anotherlocalDate)}` + +|LocalDateTime +|`isEqual(), isAfter(), isBefore()` +|Can compare two LocalDateTime objects for equality. +|`${localDateTime.isEqual(anotherlocalDateTime)} or ${localDateTime.isAfter(anotherlocalDateTime)} or ${localDateTime.isBefore(anotherlocalDateTime)}` + +|LocalTime +|`isEqual(), isAfter(), isBefore()` +|Can compare two LocalTime objects for equality. +|`${localTime.isEqual(anotherlocalTime)} or ${localTime.isAfter(anotherlocalTime)} or ${localTime.isBefore(anotherlocalTime)}` diff --git a/src/main/java/no/api/freemarker/java8/time/LocalDateAdapter.java b/src/main/java/no/api/freemarker/java8/time/LocalDateAdapter.java index 4cc5a82..1fd102a 100644 --- a/src/main/java/no/api/freemarker/java8/time/LocalDateAdapter.java +++ b/src/main/java/no/api/freemarker/java8/time/LocalDateAdapter.java @@ -69,6 +69,7 @@ public LocalDateChecker(LocalDate obj, String method) { this.method = method; } + @SuppressWarnings("Duplicates") @Override public Object exec(List list) throws TemplateModelException { LocalDateAdapter adapter = (LocalDateAdapter) list.get(0); diff --git a/src/main/java/no/api/freemarker/java8/time/LocalDateTimeAdapter.java b/src/main/java/no/api/freemarker/java8/time/LocalDateTimeAdapter.java index 31ed337..f1e6088 100644 --- a/src/main/java/no/api/freemarker/java8/time/LocalDateTimeAdapter.java +++ b/src/main/java/no/api/freemarker/java8/time/LocalDateTimeAdapter.java @@ -70,6 +70,7 @@ public LocalDateTimeChecker(LocalDateTime obj, String method) { this.method = method; } + @SuppressWarnings("Duplicates") @Override public Object exec(List list) throws TemplateModelException { LocalDateTimeAdapter adapter = (LocalDateTimeAdapter) list.get(0); diff --git a/src/test/java/no/api/freemarker/java8/time/DateTimeStepdefs.java b/src/test/java/no/api/freemarker/java8/time/DateTimeStepdefs.java index 43672cd..732693f 100644 --- a/src/test/java/no/api/freemarker/java8/time/DateTimeStepdefs.java +++ b/src/test/java/no/api/freemarker/java8/time/DateTimeStepdefs.java @@ -42,6 +42,8 @@ public class DateTimeStepdefs { private Object obj; + private Object obj2; + public DateTimeStepdefs() { this.configuration = new Configuration(VERSION_2_3_23); this.configuration.setObjectWrapper(new Java8ObjectWrapper(VERSION_2_3_23)); @@ -89,9 +91,28 @@ public void expect_the_template_to_return_the_current_year() throws Throwable { } } + @Then("^expect the template to return true$") + public void expect_the_template_to_true() throws Throwable { + Writer out = process("true"); + if (!"true".equals(out.toString())) { + Assert.fail("Expected 'true', but got '" + out.toString()); + } + } + + @Then("^expect the template to return false$") + public void expect_the_template_to_false() throws Throwable { + Writer out = process("false"); + if (!"false".equals(out.toString())) { + Assert.fail("Expected 'false', but got '" + out.toString()); + } + } + private Writer process(String res) throws IOException, TemplateException { Map map = new HashMap<>(); map.put("obj", obj); + if (obj2 != null) { + map.put("obj2", obj2); + } Template t = new Template("name", new StringReader(template), configuration); Writer out = new StringWriter(); t.process(map, out); @@ -137,16 +158,31 @@ public void localdate_object_for(String arg1) throws Throwable { obj = LocalDate.parse(arg1); } + @Given("^LocalDate object2 for \"([^\"]*)\"$") + public void localdate_object2_for(String arg1) throws Throwable { + obj2 = LocalDate.parse(arg1); + } + @Given("^LocalDateTime object for \"([^\"]*)\"$") public void localdatetime_object_for(String arg1) throws Throwable { obj = LocalDateTime.parse(arg1); } + @Given("^LocalDateTime object2 for \"([^\"]*)\"$") + public void localdatetime_object2_for(String arg1) throws Throwable { + obj2 = LocalDateTime.parse(arg1); + } + @Given("^LocalTime object for \"([^\"]*)\"$") public void localtime_object_for(String arg1) throws Throwable { obj = LocalTime.parse(arg1); } + @Given("^LocalTime object2 for \"([^\"]*)\"$") + public void localtime_object2_for(String arg1) throws Throwable { + obj2 = LocalTime.parse(arg1); + } + @Given("^MonthDay object for \"([^\"]*)\"$") public void monthday_object_for(String arg1) throws Throwable { obj = MonthDay.parse(arg1); diff --git a/src/test/resources/no/api/freemarker/java8/time/comparison.feature b/src/test/resources/no/api/freemarker/java8/time/comparison.feature new file mode 100644 index 0000000..5e1a339 --- /dev/null +++ b/src/test/resources/no/api/freemarker/java8/time/comparison.feature @@ -0,0 +1,151 @@ +Feature: Test the implemented comparison functions (isEqual, isBefore and isAfter) + + # isEqual + + Scenario: Test isEqual for LocalDate + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isEqual(obj2)?c}" + And LocalDate object for "2007-12-03" + And LocalDate object2 for "2007-12-03" + Then expect the template to return true + + Scenario: Test that is isEqual for LocalDate fails + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isEqual(obj2)?c}" + And LocalDate object for "2007-12-03" + And LocalDate object2 for "2007-12-02" + Then expect the template to return false + + Scenario: Test isEqual for LocalDateTime + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isEqual(obj2)?c}" + And LocalDateTime object for "2007-12-03T10:15:30" + And LocalDateTime object2 for "2007-12-03T10:15:30" + Then expect the template to return true + + Scenario: Test that is isEqual for LocalDateTime fails + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isEqual(obj2)?c}" + And LocalDateTime object for "2007-12-03T10:15:30" + And LocalDateTime object2 for "2007-12-03T10:15:31" + Then expect the template to return false + + Scenario: Test isEqual for LocalTime + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isEqual(obj2)?c}" + And LocalTime object for "23:44" + And LocalTime object2 for "23:44" + Then expect the template to return true + + Scenario: Test that is isEqual for LocalTime fails + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isEqual(obj2)?c}" + And LocalTime object for "23:44" + And LocalTime object2 for "23:45" + Then expect the template to return false + + # isAfter + + Scenario: Test isAfter for LocalDate + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isAfter(obj2)?c}" + And LocalDate object for "2017-12-04" + And LocalDate object2 for "2007-12-03" + Then expect the template to return true + + Scenario: Test that is isAfter for LocalDate fails + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isAfter(obj2)?c}" + And LocalDate object for "2007-12-01" + And LocalDate object2 for "2007-12-02" + Then expect the template to return false + + Scenario: Test isAfter for LocalDateTime + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isAfter(obj2)?c}" + And LocalDateTime object for "2007-12-04T10:15:30" + And LocalDateTime object2 for "2007-12-03T10:15:30" + Then expect the template to return true + + Scenario: Test that is isAfter for LocalDateTime fails + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isAfter(obj2)?c}" + And LocalDateTime object for "2007-12-01T10:15:30" + And LocalDateTime object2 for "2007-12-03T10:15:31" + Then expect the template to return false + + Scenario: Test isAfter for LocalTime + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isAfter(obj2)?c}" + And LocalTime object for "23:45" + And LocalTime object2 for "23:44" + Then expect the template to return true + + Scenario: Test that is isAfter for LocalTime fails + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isAfter(obj2)?c}" + And LocalTime object for "23:44" + And LocalTime object2 for "23:45" + Then expect the template to return false + + # isBefore + + Scenario: Test isBefore for LocalDate + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isBefore(obj2)?c}" + And LocalDate object for "2002-12-01" + And LocalDate object2 for "2007-12-03" + Then expect the template to return true + + Scenario: Test that is isBefore for LocalDate fails + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isBefore(obj2)?c}" + And LocalDate object for "2007-12-03" + And LocalDate object2 for "2007-12-02" + Then expect the template to return false + + Scenario: Test isBefore for LocalDateTime + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isBefore(obj2)?c}" + And LocalDateTime object for "2007-12-01T10:15:30" + And LocalDateTime object2 for "2007-12-03T10:15:30" + Then expect the template to return true + + Scenario: Test that is isBefore for LocalDateTime fails + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isBefore(obj2)?c}" + And LocalDateTime object for "2007-12-06T10:15:30" + And LocalDateTime object2 for "2007-12-03T10:15:31" + Then expect the template to return false + + Scenario: Test isBefore for LocalTime + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isBefore(obj2)?c}" + And LocalTime object for "22:41" + And LocalTime object2 for "23:44" + Then expect the template to return true + + Scenario: Test that is isBefore for LocalTime fails + Given an freemarker environment with locale set to "no-NO" + And timezone set to "Europe/Oslo" + And a template "${obj.isBefore(obj2)?c}" + And LocalTime object for "23:54" + And LocalTime object2 for "23:45" + Then expect the template to return false \ No newline at end of file