From 0ef619dc721a88d2269d3771ac221590be0f54d9 Mon Sep 17 00:00:00 2001 From: Dhruv Joshi Date: Thu, 30 Oct 2025 22:46:09 +0530 Subject: [PATCH 1/5] #481 Provide support for LocalDate property type --- .../properties/LocalDateProperty.java | 22 ++++++ .../properties/types/TemporalType.java | 62 ++++++++++++++++ .../properties/LocalDatePropertyTest.java | 70 +++++++++++++++++++ .../properties/types/TemporalTypeTest.java | 64 +++++++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 src/main/java/ch/jalu/configme/properties/LocalDateProperty.java create mode 100644 src/main/java/ch/jalu/configme/properties/types/TemporalType.java create mode 100644 src/test/java/ch/jalu/configme/properties/LocalDatePropertyTest.java create mode 100644 src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java diff --git a/src/main/java/ch/jalu/configme/properties/LocalDateProperty.java b/src/main/java/ch/jalu/configme/properties/LocalDateProperty.java new file mode 100644 index 00000000..9b1d79d2 --- /dev/null +++ b/src/main/java/ch/jalu/configme/properties/LocalDateProperty.java @@ -0,0 +1,22 @@ +package ch.jalu.configme.properties; + +import ch.jalu.configme.properties.types.TemporalType; +import org.jetbrains.annotations.NotNull; + +import java.time.LocalDate; + +/** + * LocalDate property. + */ +public class LocalDateProperty extends TypeBasedProperty { + + /** + * Constructor. + * + * @param path the path of the property + * @param defaultValue the default value of the property + */ + public LocalDateProperty(@NotNull String path, @NotNull LocalDate defaultValue) { + super(path, TemporalType.LOCAL_DATE, defaultValue); + } +} diff --git a/src/main/java/ch/jalu/configme/properties/types/TemporalType.java b/src/main/java/ch/jalu/configme/properties/types/TemporalType.java new file mode 100644 index 00000000..31ccab93 --- /dev/null +++ b/src/main/java/ch/jalu/configme/properties/types/TemporalType.java @@ -0,0 +1,62 @@ +package ch.jalu.configme.properties.types; + +import ch.jalu.configme.properties.convertresult.ConvertErrorRecorder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.Temporal; +import java.util.Arrays; +import java.util.List; +import java.util.function.BiFunction; + +/** + * Property type and mapper leaf type for temporal values. + * + * @param the expected temporal type + */ +public class TemporalType extends PropertyAndLeafType { + + /** Local Date temporal type. */ + public static final TemporalType LOCAL_DATE = new TemporalType<>( + LocalDate.class, Arrays.asList("yyyy-MM-dd", "dd.MM.yyyy", "MM/dd/yyyy"), LocalDate::parse); + + private final List supportedFormats; + private final BiFunction temporalParser; + + protected TemporalType(Class clazz, List supportedFormats, + BiFunction defaultParser) { + super(clazz); + this.supportedFormats = supportedFormats; + this.temporalParser = defaultParser; + } + + @Override + public @Nullable T convert(@Nullable Object object, @NotNull ConvertErrorRecorder errorRecorder) { + if (!(object instanceof String)) { + return null; + } + return convertToTemporalType((String) object); + } + + @Override + public @Nullable Object toExportValue(@NotNull T value) { + if (value instanceof LocalDate) { + return LocalDate.from(value).toString(); + } + return value.toString(); + } + + private T convertToTemporalType(String temporalText) { + for (String format: this.supportedFormats) { + try { + return this.temporalParser.apply(temporalText, DateTimeFormatter.ofPattern(format)); + } catch (DateTimeParseException e) { + // try next format + } + } + return null; + } +} diff --git a/src/test/java/ch/jalu/configme/properties/LocalDatePropertyTest.java b/src/test/java/ch/jalu/configme/properties/LocalDatePropertyTest.java new file mode 100644 index 00000000..fbdfe029 --- /dev/null +++ b/src/test/java/ch/jalu/configme/properties/LocalDatePropertyTest.java @@ -0,0 +1,70 @@ +package ch.jalu.configme.properties; + +import ch.jalu.configme.properties.convertresult.PropertyValue; +import ch.jalu.configme.resource.PropertyReader; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalDate; + +import static ch.jalu.configme.TestUtils.isErrorValueOf; +import static ch.jalu.configme.TestUtils.isValidValueOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Test for {@link LocalDateProperty}. + */ +@ExtendWith(MockitoExtension.class) +class LocalDatePropertyTest { + + private static PropertyReader reader; + + @BeforeAll + static void setUpConfiguration() { + reader = mock(PropertyReader.class); + when(reader.getObject("local-date.path.test")).thenReturn("1999-01-31"); + when(reader.getObject("local-date.path.wrong")).thenReturn("31-01-1999"); + } + + @Test + void shouldGetLocalDateValue() { + // given + Property property = new LocalDateProperty("local-date.path.test", LocalDate.of(1970, 1, 31)); + + // when + PropertyValue result = property.determineValue(reader); + + //then + assertThat(result, isValidValueOf(LocalDate.of(1999, 1, 31))); + } + + @Test + void shouldGetLocalDateDefault() { + // given + LocalDate defaultValue = LocalDate.of(1970, 1, 31); + Property property = new LocalDateProperty("local-date.path.wrong", defaultValue); + + // when + PropertyValue result = property.determineValue(reader); + + // then + assertThat(result, isErrorValueOf(defaultValue)); + } + + @Test + void shouldReturnValueForExport() { + // given + Property property = new LocalDateProperty("export.path.local-date", LocalDate.of(1970, 1, 31)); + + // when + Object exportValue = property.toExportValue(LocalDate.of(2000, 12, 31)); + + // then + assertThat(exportValue, equalTo("2000-12-31")); + } +} diff --git a/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java b/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java new file mode 100644 index 00000000..48e81001 --- /dev/null +++ b/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java @@ -0,0 +1,64 @@ +package ch.jalu.configme.properties.types; + +import ch.jalu.configme.properties.convertresult.ConvertErrorRecorder; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; + +import static ch.jalu.typeresolver.TypeInfo.of; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; + +/** + * Test for {@link TemporalType}. + */ +public class TemporalTypeTest { + + @Test + void shouldConvertToGivenTemporalTypeForSupportedFormat() { + // given + ConvertErrorRecorder errorRecorder = new ConvertErrorRecorder(); + LocalDate localDate = LocalDate.of(1970, 1, 31); + + // when / then + assertThat(TemporalType.LOCAL_DATE.convert("1970-01-31", of(LocalDate.class), errorRecorder), equalTo(localDate)); + assertThat(TemporalType.LOCAL_DATE.convert("31.01.1970", of(LocalDate.class), errorRecorder), equalTo(localDate)); + assertThat(TemporalType.LOCAL_DATE.convert("01/31/1970", of(LocalDate.class), errorRecorder), equalTo(localDate)); + } + + @Test + void shouldReturnNullForUnsupportedFormat() { + // given + ConvertErrorRecorder errorRecorder = new ConvertErrorRecorder(); + + // when / then + assertThat(TemporalType.LOCAL_DATE.convert("31-01-1970", of(LocalDate.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_DATE.convert("1970.01.31", of(LocalDate.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_DATE.convert("1970/01/31", of(LocalDate.class), errorRecorder), nullValue()); + } + + @Test + void shouldReturnNullForInvalidValue() { + // given + ConvertErrorRecorder errorRecorder = new ConvertErrorRecorder(); + + // when / then + assertThat(TemporalType.LOCAL_DATE.convert("test", of(LocalDate.class), errorRecorder), nullValue()); + } + + @Test + void shouldReturnNullForNull() { + // given + ConvertErrorRecorder errorRecorder = new ConvertErrorRecorder(); + + // when / then + assertThat(TemporalType.LOCAL_DATE.convert(null, of(LocalDate.class), errorRecorder), nullValue()); + } + + @Test + void shouldExportValueAsString() { + // given / when / then + assertThat(TemporalType.LOCAL_DATE.toExportValue(LocalDate.of(1970, 1, 31)), equalTo("1970-01-31")); + } +} From 8d789bb9a2b41f4bf67436c051ecc266df1d2f05 Mon Sep 17 00:00:00 2001 From: Dhruv Joshi Date: Fri, 12 Dec 2025 00:14:56 +0530 Subject: [PATCH 2/5] #481 Enhance TemporalType with matching export format along with additional LocalTime/LocalDateTime types --- .../properties/types/TemporalType.java | 34 ++++++++--- .../properties/types/TemporalTypeTest.java | 56 ++++++++++++++++++- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/main/java/ch/jalu/configme/properties/types/TemporalType.java b/src/main/java/ch/jalu/configme/properties/types/TemporalType.java index 31ccab93..494007c7 100644 --- a/src/main/java/ch/jalu/configme/properties/types/TemporalType.java +++ b/src/main/java/ch/jalu/configme/properties/types/TemporalType.java @@ -5,6 +5,8 @@ import org.jetbrains.annotations.Nullable; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.time.temporal.Temporal; @@ -22,15 +24,34 @@ public class TemporalType extends PropertyAndLeafType { /** Local Date temporal type. */ public static final TemporalType LOCAL_DATE = new TemporalType<>( LocalDate.class, Arrays.asList("yyyy-MM-dd", "dd.MM.yyyy", "MM/dd/yyyy"), LocalDate::parse); + /** Local Time temporal type. */ + public static final TemporalType LOCAL_TIME = new TemporalType<>( + LocalTime.class, Arrays.asList("HH:mm:ss", "HH.mm", "HH:mm"), LocalTime::parse); + /** Local Date Time temporal type. */ + public static final TemporalType LOCAL_DATE_TIME = new TemporalType<>( + LocalDateTime.class, Arrays.asList("yyyy-MM-dd HH:mm:ss", "dd.MM.yyyy HH:mm:ss", "MM/dd/yyyy HH:mm:ss"), + LocalDateTime::parse); private final List supportedFormats; private final BiFunction temporalParser; + private String defaultExportFormat; - protected TemporalType(Class clazz, List supportedFormats, - BiFunction defaultParser) { + /** + * Constructor. + * + * @param clazz the temporal type this type should convert to + * @param supportedFormats list of conversion formats supported for this type + * @param defaultParser function which can parse a value to this type in one of the given supportedFormats + */ + public TemporalType(@NotNull Class clazz, @NotNull List supportedFormats, + @NotNull BiFunction defaultParser) { super(clazz); + if (supportedFormats.isEmpty()) { + throw new IllegalArgumentException("At least one supported format must be provided."); + } this.supportedFormats = supportedFormats; this.temporalParser = defaultParser; + this.defaultExportFormat = supportedFormats.get(0); } @Override @@ -43,16 +64,15 @@ protected TemporalType(Class clazz, List supportedFormats, @Override public @Nullable Object toExportValue(@NotNull T value) { - if (value instanceof LocalDate) { - return LocalDate.from(value).toString(); - } - return value.toString(); + return DateTimeFormatter.ofPattern(this.defaultExportFormat).format(value); } private T convertToTemporalType(String temporalText) { for (String format: this.supportedFormats) { try { - return this.temporalParser.apply(temporalText, DateTimeFormatter.ofPattern(format)); + T parsedValue = this.temporalParser.apply(temporalText, DateTimeFormatter.ofPattern(format)); + this.defaultExportFormat = format; + return parsedValue; } catch (DateTimeParseException e) { // try next format } diff --git a/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java b/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java index 48e81001..7f2e7261 100644 --- a/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java +++ b/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java @@ -4,27 +4,49 @@ import org.junit.jupiter.api.Test; import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Collections; import static ch.jalu.typeresolver.TypeInfo.of; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertThrows; /** * Test for {@link TemporalType}. */ public class TemporalTypeTest { + @Test + void shouldNotAllowToInstantiateTemporalTypeWithInvalidArguments() { + // when + IllegalArgumentException noSupportedFormats = assertThrows(IllegalArgumentException.class, + () -> new TemporalType<>(LocalDate.class, Collections.emptyList(), LocalDate::parse)); + + // then + assertThat(noSupportedFormats.getMessage(), matchesPattern("At least one supported format must be provided.")); + } + @Test void shouldConvertToGivenTemporalTypeForSupportedFormat() { // given ConvertErrorRecorder errorRecorder = new ConvertErrorRecorder(); LocalDate localDate = LocalDate.of(1970, 1, 31); + LocalTime localTimeFull = LocalTime.of(15, 35, 40); + LocalTime localTimeShort = LocalTime.of(15, 35); + LocalDateTime localDateTime = LocalDateTime.of(1970, 1, 31, 15, 35, 40); // when / then assertThat(TemporalType.LOCAL_DATE.convert("1970-01-31", of(LocalDate.class), errorRecorder), equalTo(localDate)); assertThat(TemporalType.LOCAL_DATE.convert("31.01.1970", of(LocalDate.class), errorRecorder), equalTo(localDate)); assertThat(TemporalType.LOCAL_DATE.convert("01/31/1970", of(LocalDate.class), errorRecorder), equalTo(localDate)); + assertThat(TemporalType.LOCAL_TIME.convert("15:35:40", of(LocalTime.class), errorRecorder), equalTo(localTimeFull)); + assertThat(TemporalType.LOCAL_TIME.convert("15.35", of(LocalTime.class), errorRecorder), equalTo(localTimeShort)); + assertThat(TemporalType.LOCAL_TIME.convert("15:35", of(LocalTime.class), errorRecorder), equalTo(localTimeShort)); + assertThat(TemporalType.LOCAL_DATE_TIME.convert("1970-01-31 15:35:40", of(LocalDateTime.class), errorRecorder), equalTo(localDateTime)); + assertThat(TemporalType.LOCAL_DATE_TIME.convert("31.01.1970 15:35:40", of(LocalDateTime.class), errorRecorder), equalTo(localDateTime)); + assertThat(TemporalType.LOCAL_DATE_TIME.convert("01/31/1970 15:35:40", of(LocalDateTime.class), errorRecorder), equalTo(localDateTime)); } @Test @@ -36,6 +58,13 @@ void shouldReturnNullForUnsupportedFormat() { assertThat(TemporalType.LOCAL_DATE.convert("31-01-1970", of(LocalDate.class), errorRecorder), nullValue()); assertThat(TemporalType.LOCAL_DATE.convert("1970.01.31", of(LocalDate.class), errorRecorder), nullValue()); assertThat(TemporalType.LOCAL_DATE.convert("1970/01/31", of(LocalDate.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_TIME.convert("25:35:40", of(LocalTime.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_TIME.convert("15.35.40", of(LocalTime.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_TIME.convert("15:35.40", of(LocalTime.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_TIME.convert("15.35:40", of(LocalTime.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_DATE_TIME.convert("31-01-1970 25:35:40", of(LocalDateTime.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_DATE_TIME.convert("1970.01.31 15.35.40", of(LocalDateTime.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_DATE_TIME.convert("1970/01/31 15.35:40", of(LocalDateTime.class), errorRecorder), nullValue()); } @Test @@ -45,6 +74,8 @@ void shouldReturnNullForInvalidValue() { // when / then assertThat(TemporalType.LOCAL_DATE.convert("test", of(LocalDate.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_TIME.convert("test", of(LocalTime.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_DATE_TIME.convert("test", of(LocalDateTime.class), errorRecorder), nullValue()); } @Test @@ -54,11 +85,32 @@ void shouldReturnNullForNull() { // when / then assertThat(TemporalType.LOCAL_DATE.convert(null, of(LocalDate.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_TIME.convert(null, of(LocalTime.class), errorRecorder), nullValue()); + assertThat(TemporalType.LOCAL_DATE_TIME.convert(null, of(LocalDateTime.class), errorRecorder), nullValue()); } @Test void shouldExportValueAsString() { // given / when / then assertThat(TemporalType.LOCAL_DATE.toExportValue(LocalDate.of(1970, 1, 31)), equalTo("1970-01-31")); + assertThat(TemporalType.LOCAL_TIME.toExportValue(LocalTime.of(13, 55, 13)), equalTo("13:55:13")); + assertThat(TemporalType.LOCAL_DATE_TIME.toExportValue(LocalDateTime.of(1970, 1, 31, 12, 30, 47)), equalTo("1970-01-31 12:30:47")); + } + + @Test + void shouldExportValueInMatchingFormatAsString() { + // given + ConvertErrorRecorder errorRecorder = new ConvertErrorRecorder(); + LocalDate localDate = TemporalType.LOCAL_DATE.convert("01/31/1970", errorRecorder); + LocalTime localTime = TemporalType.LOCAL_TIME.convert("11:11", errorRecorder); + LocalDateTime localDateTime = TemporalType.LOCAL_DATE_TIME.convert("31.01.1970 13:55:13", errorRecorder); + + // when / then + assertThat(localDate, notNullValue()); + assertThat(localTime, notNullValue()); + assertThat(localDateTime, notNullValue()); + assertThat(TemporalType.LOCAL_DATE.toExportValue(localDate), equalTo("01/31/1970")); + assertThat(TemporalType.LOCAL_TIME.toExportValue(localTime), equalTo("11:11")); + assertThat(TemporalType.LOCAL_DATE_TIME.toExportValue(localDateTime), equalTo("31.01.1970 13:55:13")); } } From 1f3d43dec816aa72f39dd8a3ae8d9afe789ebe6f Mon Sep 17 00:00:00 2001 From: Dhruv Joshi Date: Fri, 12 Dec 2025 00:41:31 +0530 Subject: [PATCH 3/5] #481 Provide support for LocalTime and LocalDateTime property type --- .../properties/LocalDateProperty.java | 2 +- .../properties/LocalDateTimeProperty.java | 22 ++++++ .../properties/LocalTimeProperty.java | 22 ++++++ .../properties/LocalDateTimePropertyTest.java | 70 +++++++++++++++++++ .../properties/LocalTimePropertyTest.java | 70 +++++++++++++++++++ 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ch/jalu/configme/properties/LocalDateTimeProperty.java create mode 100644 src/main/java/ch/jalu/configme/properties/LocalTimeProperty.java create mode 100644 src/test/java/ch/jalu/configme/properties/LocalDateTimePropertyTest.java create mode 100644 src/test/java/ch/jalu/configme/properties/LocalTimePropertyTest.java diff --git a/src/main/java/ch/jalu/configme/properties/LocalDateProperty.java b/src/main/java/ch/jalu/configme/properties/LocalDateProperty.java index 9b1d79d2..880c4c7b 100644 --- a/src/main/java/ch/jalu/configme/properties/LocalDateProperty.java +++ b/src/main/java/ch/jalu/configme/properties/LocalDateProperty.java @@ -6,7 +6,7 @@ import java.time.LocalDate; /** - * LocalDate property. + * {@link LocalDate} property. */ public class LocalDateProperty extends TypeBasedProperty { diff --git a/src/main/java/ch/jalu/configme/properties/LocalDateTimeProperty.java b/src/main/java/ch/jalu/configme/properties/LocalDateTimeProperty.java new file mode 100644 index 00000000..612664ec --- /dev/null +++ b/src/main/java/ch/jalu/configme/properties/LocalDateTimeProperty.java @@ -0,0 +1,22 @@ +package ch.jalu.configme.properties; + +import ch.jalu.configme.properties.types.TemporalType; +import org.jetbrains.annotations.NotNull; + +import java.time.LocalDateTime; + +/** + * {@link LocalDateTime} property. + */ +public class LocalDateTimeProperty extends TypeBasedProperty { + + /** + * Constructor. + * + * @param path the path of the property + * @param defaultValue the default value of the property + */ + public LocalDateTimeProperty(@NotNull String path, @NotNull LocalDateTime defaultValue) { + super(path, TemporalType.LOCAL_DATE_TIME, defaultValue); + } +} diff --git a/src/main/java/ch/jalu/configme/properties/LocalTimeProperty.java b/src/main/java/ch/jalu/configme/properties/LocalTimeProperty.java new file mode 100644 index 00000000..5f6349a8 --- /dev/null +++ b/src/main/java/ch/jalu/configme/properties/LocalTimeProperty.java @@ -0,0 +1,22 @@ +package ch.jalu.configme.properties; + +import ch.jalu.configme.properties.types.TemporalType; +import org.jetbrains.annotations.NotNull; + +import java.time.LocalTime; + +/** + * {@link LocalTime} property. + */ +public class LocalTimeProperty extends TypeBasedProperty { + + /** + * Constructor. + * + * @param path the path of the property + * @param defaultValue the default value of the property + */ + public LocalTimeProperty(@NotNull String path, @NotNull LocalTime defaultValue) { + super(path, TemporalType.LOCAL_TIME, defaultValue); + } +} diff --git a/src/test/java/ch/jalu/configme/properties/LocalDateTimePropertyTest.java b/src/test/java/ch/jalu/configme/properties/LocalDateTimePropertyTest.java new file mode 100644 index 00000000..868bede4 --- /dev/null +++ b/src/test/java/ch/jalu/configme/properties/LocalDateTimePropertyTest.java @@ -0,0 +1,70 @@ +package ch.jalu.configme.properties; + +import ch.jalu.configme.properties.convertresult.PropertyValue; +import ch.jalu.configme.resource.PropertyReader; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalDateTime; + +import static ch.jalu.configme.TestUtils.isErrorValueOf; +import static ch.jalu.configme.TestUtils.isValidValueOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Test for {@link LocalDateTimeProperty}. + */ +@ExtendWith(MockitoExtension.class) +class LocalDateTimePropertyTest { + + private static PropertyReader reader; + + @BeforeAll + static void setUpConfiguration() { + reader = mock(PropertyReader.class); + when(reader.getObject("local-date-time.path.test")).thenReturn("2001-10-20 19:55:30"); + when(reader.getObject("local-date-time.path.wrong")).thenReturn("20-2001-10 55:19:30"); + } + + @Test + void shouldGetLocalDateTimeValue() { + // given + Property property = new LocalDateTimeProperty("local-date-time.path.test", LocalDateTime.of(1970, 1, 31, 12, 0)); + + // when + PropertyValue result = property.determineValue(reader); + + // then + assertThat(result, isValidValueOf(LocalDateTime.of(2001, 10, 20, 19, 55, 30))); + } + + @Test + void shouldGetLocalDateTimeDefault() { + // given + LocalDateTime defaultDateTime = LocalDateTime.of(1970, 1, 31, 12, 0); + Property property = new LocalDateTimeProperty("local-date-time.path.wrong", defaultDateTime); + + // when + PropertyValue result = property.determineValue(reader); + + // then + assertThat(result, isErrorValueOf(defaultDateTime)); + } + + @Test + void shouldReturnValueForExport() { + // given + Property property = new LocalDateTimeProperty("export.path.local-date-time", LocalDateTime.of(1970, 1, 31, 12, 0)); + + // when + Object exportedValue = property.toExportValue(LocalDateTime.of(2001, 10, 22, 11, 39, 42)); + + // then + assertThat(exportedValue, equalTo("2001-10-22 11:39:42")); + } +} diff --git a/src/test/java/ch/jalu/configme/properties/LocalTimePropertyTest.java b/src/test/java/ch/jalu/configme/properties/LocalTimePropertyTest.java new file mode 100644 index 00000000..567fa925 --- /dev/null +++ b/src/test/java/ch/jalu/configme/properties/LocalTimePropertyTest.java @@ -0,0 +1,70 @@ +package ch.jalu.configme.properties; + +import ch.jalu.configme.properties.convertresult.PropertyValue; +import ch.jalu.configme.resource.PropertyReader; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalTime; + +import static ch.jalu.configme.TestUtils.isErrorValueOf; +import static ch.jalu.configme.TestUtils.isValidValueOf; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Test for {@link LocalTimeProperty}. + */ +@ExtendWith(MockitoExtension.class) +class LocalTimePropertyTest { + + private static PropertyReader reader; + + @BeforeAll + static void setUpConfiguration() { + reader = mock(PropertyReader.class); + when(reader.getObject("local-time.path.test")).thenReturn("19:55:30"); + when(reader.getObject("local-time.path.wrong")).thenReturn("55:19:30"); + } + + @Test + void shouldGetLocalTimeValue() { + // given + Property property = new LocalTimeProperty("local-time.path.test", LocalTime.of(12, 0)); + + // when + PropertyValue result = property.determineValue(reader); + + // then + assertThat(result, isValidValueOf(LocalTime.of(19, 55, 30))); + } + + @Test + void shouldGetLocalTimeDefault() { + // given + LocalTime defaultTime = LocalTime.of(12, 0); + Property property = new LocalTimeProperty("local-time.path.wrong", defaultTime); + + // when + PropertyValue result = property.determineValue(reader); + + // then + assertThat(result, isErrorValueOf(defaultTime)); + } + + @Test + void shouldReturnValueForExport() { + // given + Property property = new LocalTimeProperty("export.path.local-time", LocalTime.of(23, 59)); + + // when + Object exportedValue = property.toExportValue(LocalTime.of(19, 55)); + + // then + assertThat(exportedValue, equalTo("19:55:00")); + } +} From aac8c1c6b8a70d7170036923856771d5da520112 Mon Sep 17 00:00:00 2001 From: Dhruv Joshi Date: Fri, 12 Dec 2025 18:08:11 +0530 Subject: [PATCH 4/5] #481 Support default TemporalTypes for bean mapper --- .../leafvaluehandler/LeafValueHandlerImpl.java | 6 +++++- .../leafvaluehandler/LeafValueHandlerImplTest.java | 12 ++++++------ .../configme/properties/types/TemporalTypeTest.java | 5 ++++- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/ch/jalu/configme/beanmapper/leafvaluehandler/LeafValueHandlerImpl.java b/src/main/java/ch/jalu/configme/beanmapper/leafvaluehandler/LeafValueHandlerImpl.java index b238c1be..d6de6ce1 100644 --- a/src/main/java/ch/jalu/configme/beanmapper/leafvaluehandler/LeafValueHandlerImpl.java +++ b/src/main/java/ch/jalu/configme/beanmapper/leafvaluehandler/LeafValueHandlerImpl.java @@ -7,6 +7,7 @@ import ch.jalu.configme.properties.types.NumberType; import ch.jalu.configme.properties.types.RegexType; import ch.jalu.configme.properties.types.StringType; +import ch.jalu.configme.properties.types.TemporalType; import ch.jalu.typeresolver.TypeInfo; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -74,7 +75,10 @@ public LeafValueHandlerImpl(@NotNull MapperLeafType @NotNull ... leafTypes) { NumberType.SHORT, NumberType.BIG_INTEGER, NumberType.BIG_DECIMAL, - RegexType.REGEX) + RegexType.REGEX, + TemporalType.LOCAL_DATE, + TemporalType.LOCAL_TIME, + TemporalType.LOCAL_DATE_TIME) .collect(Collectors.toCollection(ArrayList::new)); } diff --git a/src/test/java/ch/jalu/configme/beanmapper/leafvaluehandler/LeafValueHandlerImplTest.java b/src/test/java/ch/jalu/configme/beanmapper/leafvaluehandler/LeafValueHandlerImplTest.java index 39d99516..3bb5c49b 100644 --- a/src/test/java/ch/jalu/configme/beanmapper/leafvaluehandler/LeafValueHandlerImplTest.java +++ b/src/test/java/ch/jalu/configme/beanmapper/leafvaluehandler/LeafValueHandlerImplTest.java @@ -9,12 +9,12 @@ import ch.jalu.configme.properties.types.NumberType; import ch.jalu.configme.properties.types.RegexType; import ch.jalu.configme.properties.types.StringType; +import ch.jalu.configme.properties.types.TemporalType; import ch.jalu.typeresolver.typeimpl.WildcardTypeImpl; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; -import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -59,7 +59,7 @@ void shouldReturnDefaultLeafTypes() { List leafTypes = LeafValueHandlerImpl.createDefaultLeafTypes(); // then - assertThat(leafTypes, hasSize(12)); + assertThat(leafTypes, hasSize(15)); assertThat(leafTypes.get(0), sameInstance(BooleanType.BOOLEAN)); assertThat(leafTypes.get(1), sameInstance(StringType.STRING)); assertThat(leafTypes.get(2), sameInstance(NumberType.INTEGER)); @@ -72,6 +72,9 @@ void shouldReturnDefaultLeafTypes() { assertThat(leafTypes.get(9), sameInstance(NumberType.BIG_INTEGER)); assertThat(leafTypes.get(10), sameInstance(NumberType.BIG_DECIMAL)); assertThat(leafTypes.get(11), sameInstance(RegexType.REGEX)); + assertThat(leafTypes.get(12), sameInstance(TemporalType.LOCAL_DATE)); + assertThat(leafTypes.get(13), sameInstance(TemporalType.LOCAL_TIME)); + assertThat(leafTypes.get(14), sameInstance(TemporalType.LOCAL_DATE_TIME)); } @Test @@ -96,7 +99,7 @@ void shouldCreateValueHandlerWithBuilder() { .addType(leafType1) .addDefaults() .addType(leafType2) - .removeMatchingTypes(type -> type instanceof NumberType) + .removeMatchingTypes(type -> type instanceof NumberType || type instanceof TemporalType) .build(); // then @@ -166,13 +169,11 @@ void shouldNotConvertForUnsupportedTargetTypes() { Object object = "2020-02-13"; ConvertErrorRecorder errorRecorder = mock(ConvertErrorRecorder.class); - MappingContext dateContext = MappingContextImpl.createRoot(of(LocalDate.class), errorRecorder); MappingContext wildcardContext = MappingContextImpl.createRoot(of(WildcardTypeImpl.newUnboundedWildcard()), errorRecorder); LeafValueHandlerImpl leafValueHandler = new LeafValueHandlerImpl(LeafValueHandlerImpl.createDefaultLeafTypes()); // when / then - assertThat(leafValueHandler.convert(object, dateContext), nullValue()); assertThat(leafValueHandler.convert(object, wildcardContext), nullValue()); } @@ -194,7 +195,6 @@ void shouldNotConvertUnsupportedValuesToExportValues() { ExportContext exportContext = ExportContextImpl.createRoot(); // when / then - assertThat(leafValueHandler.toExportValue(LocalDate.now(), exportContext), nullValue()); assertThat(leafValueHandler.toExportValue(new Object(), exportContext), nullValue()); } diff --git a/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java b/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java index 7f2e7261..94be4dd3 100644 --- a/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java +++ b/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java @@ -10,7 +10,10 @@ import static ch.jalu.typeresolver.TypeInfo.of; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.matchesPattern; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertThrows; /** From 887d71434e433fe065c4e4b3fe91c36476df449f Mon Sep 17 00:00:00 2001 From: Dhruv Joshi Date: Mon, 15 Dec 2025 22:08:52 +0530 Subject: [PATCH 5/5] #481 Reset state of all default temporal type unit tests --- .../configme/properties/types/TemporalTypeTest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java b/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java index 94be4dd3..f1a862f1 100644 --- a/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java +++ b/src/test/java/ch/jalu/configme/properties/types/TemporalTypeTest.java @@ -1,7 +1,10 @@ package ch.jalu.configme.properties.types; import ch.jalu.configme.properties.convertresult.ConvertErrorRecorder; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import java.time.LocalDate; import java.time.LocalDateTime; @@ -19,8 +22,17 @@ /** * Test for {@link TemporalType}. */ +@ExtendWith(MockitoExtension.class) public class TemporalTypeTest { + @AfterAll + static void reset() { + ConvertErrorRecorder errorRecorder = new ConvertErrorRecorder(); + TemporalType.LOCAL_DATE.convert("1970-01-01", errorRecorder); + TemporalType.LOCAL_TIME.convert("12:00:00", errorRecorder); + TemporalType.LOCAL_DATE_TIME.convert("1970-01-01 12:00:00", errorRecorder); + } + @Test void shouldNotAllowToInstantiateTemporalTypeWithInvalidArguments() { // when