This is an automated email from the ASF dual-hosted git repository. gk pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/turbine-core.git
commit 35f7dd5988a60086c6fd37424c82c52f7ac278cc Author: Georg Kallidis <[email protected]> AuthorDate: Mon Dec 6 14:22:52 2021 +0100 - Add java.time DateTimeFormatter service, tool and test --- conf/test/CompleteTurbineResources.properties | 9 + .../localization/DateTimeFormatterServiceTest.java | 259 +++++++++++++++++++++ 2 files changed, 268 insertions(+) diff --git a/conf/test/CompleteTurbineResources.properties b/conf/test/CompleteTurbineResources.properties index 624c0fd..50db826 100644 --- a/conf/test/CompleteTurbineResources.properties +++ b/conf/test/CompleteTurbineResources.properties @@ -329,6 +329,10 @@ services.TemplateService.classname=org.apache.turbine.services.template.TurbineT services.UIService.classname = org.apache.turbine.services.ui.TurbineUIService # services.SessionService.classname=org.apache.turbine.services.session.TurbineSessionService +services.DateTimeFormatterService.classname= org.apache.turbine.services.localization.DateTimeFormatterService + +services.DateTimeFormatterService.earlyInit=true + # Turn on the appropriate template service. services.VelocityService.classname=org.apache.turbine.services.velocity.TurbineVelocityService @@ -472,10 +476,12 @@ tool.request.l10n=org.apache.turbine.services.localization.LocalizationTool # This pull tool is to allow for easy formatting of Date object into Strings tool.request.dateFormatter=org.apache.turbine.services.pull.util.DateFormatter + # Use this tool if you need a place to store data that will persist between # requests. Any data stored using this tool will be stored in the session. tool.session.sessionData=org.apache.turbine.services.pull.util.SessionData + # These are intake tools. # tool.request.intake=org.apache.turbine.services.intake.IntakeTool @@ -485,6 +491,9 @@ tool.session.sessionData=org.apache.turbine.services.pull.util.SessionData # This pull tool can be used to provide skins to an application tool.global.ui = org.apache.turbine.services.pull.tools.UITool +# This pull tool is to allow for easy formatting of Datetime / TemporalAccessor object into Strings +tool.global.dateTimeFormatter=org.apache.turbine.services.pull.util.DateTimeFormatterTool + # # These properties apply to both the old UIManager and the newer UIService tool.ui.dir.skin = /turbine-skins/ tool.ui.dir.image = /turbine-images/ diff --git a/src/test/org/apache/turbine/services/localization/DateTimeFormatterServiceTest.java b/src/test/org/apache/turbine/services/localization/DateTimeFormatterServiceTest.java new file mode 100644 index 0000000..87970c4 --- /dev/null +++ b/src/test/org/apache/turbine/services/localization/DateTimeFormatterServiceTest.java @@ -0,0 +1,259 @@ +package org.apache.turbine.services.localization; + + +import org.apache.turbine.annotation.AnnotationProcessor; +import org.apache.turbine.annotation.TurbineService; +import org.apache.turbine.services.pull.PullService; +import org.apache.turbine.services.pull.util.DateTimeFormatterTool; +import org.apache.turbine.util.TurbineConfig; +import org.apache.turbine.util.TurbineException; +import org.apache.velocity.context.Context; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DynamicNode; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.TestInstance; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoField; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAccessor; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.DynamicContainer.dynamicContainer; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; + +/** + * Test class for DateTimeFormatter. + * + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class DateTimeFormatterServiceTest { + + @TurbineService + private DateTimeFormatterService df; + + private TurbineConfig tc = null; + + @TurbineService + private PullService pullService; + + DateTimeFormatterTool dateTimeFormatterTool; + + @BeforeAll + public void setup() throws TurbineException + { + // required to initialize defaults + tc = new TurbineConfig( + ".", + "/conf/test/CompleteTurbineResources.properties"); + tc.initialize(); + + AnnotationProcessor.process(this); + + assertNotNull(pullService); + Context globalContext = pullService.getGlobalContext(); + assertNotNull(globalContext); + + dateTimeFormatterTool = (DateTimeFormatterTool) globalContext.get("dateTimeFormatter"); + assertNotNull(dateTimeFormatterTool); + } + + @AfterAll + public void tearDown() + { + tc.dispose(); + } + + /* + * Class under test for String format(Date, String) + */ + @Test + void testTool() throws TurbineException + { + assertNotNull(pullService); + Context globalContext = pullService.getGlobalContext(); + assertNotNull(globalContext); + + DateTimeFormatterTool dateTimeFormatterTool = (DateTimeFormatterTool) globalContext.get("dateTimeFormatter"); + assertNotNull(dateTimeFormatterTool); + } + + @TestFactory + Stream<DynamicNode> testDateTimeFormatterInstances() { + // Stream of DateTimeFormatterInterface to check + Stream<DateTimeFormatterInterface> inputStream = Stream.of(df,dateTimeFormatterTool); + // Executes tests based on the current input value. + return inputStream.map(dtf -> dynamicContainer( + "Test " + dtf + " in factory container:", + Stream.of( + dynamicTest("test formatDateString",() -> formatDateString(dtf) ), + dynamicTest("test formatZonedDateString",() -> formatZonedDateString(dtf) ), + dynamicTest("test defaultMapFromInstant",() -> defaultMapFromInstant(dtf) ), + dynamicTest("test defaultMapInstant",() -> defaultMapInstant(dtf) ), + dynamicTest("test mapDateStringNullString", () -> mapDateStringNullString(dtf)), + dynamicTest("test mapDateStringEmptyString",() -> mapDateStringEmptyString(dtf)), + dynamicTest("test formatDateStringNullFormat",() -> formatDateStringNullFormat(dtf)), + dynamicTest("test formatDateStringNullString",() -> formatDateStringNullString(dtf)), + dynamicTest("test formatDateStringEmptyString",() -> formatDateStringEmptyString(dtf)) + )) + ); + // Or returns a stream of dynamic tests instead of Dynamic nodes, + // but requires Function<DateTimeFormatterInterface, String> displayNameGenerator and + // e.g. ThrowingConsumer<DateTimeFormatterInterface> testExecutor = dtf + //return DynamicTest.stream(inputStream, displayNameGenerator, testExecutor); + } + + void formatDateString(DateTimeFormatterInterface dateTime) + { + LocalDateTime ldt = LocalDateTime.now(); + int day = ldt.get(ChronoField.DAY_OF_MONTH); + int month = ldt.get(ChronoField.MONTH_OF_YEAR); // one based + int year = ldt.get(ChronoField.YEAR); + + String dayString = (day < 10 ? "0" : "") + day; + String monthString = (month < 10 ? "0" : "") + month; + String ddmmyyyy = dayString + "/" + monthString + "/" + year; + + String mmddyyyy = "" + monthString + "/" + dayString + "/" + year; + + assertEquals(ddmmyyyy, dateTime.format(ldt, "dd/MM/yyyy")); + assertEquals(mmddyyyy, dateTime.format(ldt, "MM/dd/yyyy")); + } + + void formatZonedDateString(DateTimeFormatterInterface dateTime) + { + ZonedDateTime zdt = ZonedDateTime.now(); + int day = zdt.get(ChronoField.DAY_OF_MONTH); + int month = zdt.get(ChronoField.MONTH_OF_YEAR); // one based + int year = zdt.get(ChronoField.YEAR); + zdt = zdt.truncatedTo(ChronoUnit.MINUTES); + + String dayString = (day < 10 ? "0" : "") + day; + String monthString = (month < 10 ? "0" : "") + month; + String ddmmyyyy = dayString + "/" + monthString + "/" + year; + Assertions.assertEquals(ddmmyyyy, df.format(zdt, "dd/MM/yyyy")); + + int hours = zdt.get(ChronoField.HOUR_OF_DAY); + int mins = zdt.get(ChronoField.MINUTE_OF_HOUR); + int secs = zdt.get(ChronoField.SECOND_OF_MINUTE); + String hourString = (hours < 10 ? "0" : "") + hours; + String minsString = (mins < 10 ? "0" : "") + mins; + String secsString = (secs < 10 ? "0" : "") + secs; + + String zone = zdt.getZone().getId(); + String offset = zdt.getOffset().getId(); + // offset formatting not easy matchable, removed + String mmddyyyy = + "" + monthString + "/" + dayString + "/" + year + " " + hourString + ":" + minsString + ":" + secsString + " " + zone; + // zone + offset format, removed offset ZZZ + assertEquals(mmddyyyy, dateTime.format(zdt, "MM/dd/yyyy HH:mm:ss VV")); + } + + void defaultMapFromInstant(DateTimeFormatterInterface dateTime) + { + DateTimeFormatter incomingFormat = DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.systemDefault()); + // may throws an DateTimeParseException + Instant now = Instant.now().truncatedTo(ChronoUnit.MINUTES); + String source = incomingFormat.format(now); + + TemporalAccessor dateTimeFromInstant = incomingFormat.parse(source); + int day = dateTimeFromInstant.get(ChronoField.DAY_OF_MONTH); + int month = dateTimeFromInstant.get(ChronoField.MONTH_OF_YEAR); // one based + int year = dateTimeFromInstant.get(ChronoField.YEAR); + + String dayString = (day < 10 ? "0" : "") + day; + String monthString = (month < 10 ? "0" : "") + month; + String mmddyyyy = "" + monthString + "/" + dayString + "/" + year; + assertEquals(mmddyyyy, dateTime.mapFrom(source, incomingFormat)); + } + + void defaultMapInstant(DateTimeFormatterInterface dateTime) + { + String source = dateTime.format(Instant.now()); + + TemporalAccessor dateTimeFromInstant = dateTime.getDefaultFormat().parse(source); + + int day = dateTimeFromInstant.get(ChronoField.DAY_OF_MONTH); + int month = dateTimeFromInstant.get(ChronoField.MONTH_OF_YEAR); // one based + int year = dateTimeFromInstant.get(ChronoField.YEAR); + + String dayString = (day < 10 ? "0" : "") + day; + String monthString = (month < 10 ? "0" : "") + month; + String yyyymmdd = year + "-" + monthString + "-" + dayString; + + // caution we are mapping from the DateTimeFormatterTool defaultFormat-pattern without time! + // ISO_DATE_TIME will throw an error: + // java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: HourOfDay + DateTimeFormatter outgoingFormat = DateTimeFormatter.ISO_DATE.withZone(ZoneId.systemDefault()); + Assertions.assertEquals(yyyymmdd, dateTime.mapTo(source, outgoingFormat)); + + outgoingFormat = DateTimeFormatter.ISO_LOCAL_DATE.withZone(ZoneId.systemDefault()); + Assertions.assertEquals(yyyymmdd, dateTime.mapTo(source, outgoingFormat)); + + // ISO_OFFSET_DATE : Unsupported field: OffsetSeconds + // ISO_INSTANT; Unsupported field: InstantSeconds + yyyymmdd = year + monthString + dayString; + outgoingFormat = DateTimeFormatter.BASIC_ISO_DATE.withZone(ZoneId.systemDefault()); + assertEquals(yyyymmdd, dateTime.mapTo(source, outgoingFormat)); + } + /* + * Class under test for String format(null, String) + */ + void mapDateStringNullString(DateTimeFormatterInterface dateTime) + { + DateTimeFormatter outgoingFormat = DateTimeFormatter.ISO_INSTANT; + Assertions.assertEquals("", + dateTime.mapFrom(null, outgoingFormat), "null argument should produce an empty String"); + } + + /* + * Class under test for String format(Date, "") + */ + void mapDateStringEmptyString(DateTimeFormatterInterface dateTime ) + { + Instant today = Instant.now(); + String todayFormatted = df.format(today); + Assertions.assertEquals("", + dateTime.mapFrom(todayFormatted, null), "Empty pattern should map to empty String"); + } + + /* + * Class under test for String format(null, String) + */ + void formatDateStringNullString(DateTimeFormatterInterface dateTime ) + { + Assertions.assertEquals("", + dateTime.format(null, "MM/dd/xyyyy"), "null argument should produce an empty String"); + } + + /* + * Class under test for String format(Date, "") + */ + void formatDateStringEmptyString(DateTimeFormatterInterface dateTime) + { + Instant today = Instant.now(); + Assertions.assertEquals("", + dateTime.format(today, ""), "Empty pattern should produce empty String"); + } + + /* + * Class under test for String format(Date, "") + */ + + void formatDateStringNullFormat(DateTimeFormatterInterface dateTime) + { + Instant today = Instant.now(); + Assertions.assertEquals("", + dateTime.format(today, null), "null pattern should produce empty String"); + } + +}
