Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}

Expand Down
22 changes: 22 additions & 0 deletions src/main/java/ch/jalu/configme/properties/LocalDateProperty.java
Original file line number Diff line number Diff line change
@@ -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;

/**
* {@link LocalDate} property.
*/
public class LocalDateProperty extends TypeBasedProperty<LocalDate> {

/**
* 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<LocalDateTime> {

/**
* 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);
}
}
22 changes: 22 additions & 0 deletions src/main/java/ch/jalu/configme/properties/LocalTimeProperty.java
Original file line number Diff line number Diff line change
@@ -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<LocalTime> {

/**
* 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);
}
}
82 changes: 82 additions & 0 deletions src/main/java/ch/jalu/configme/properties/types/TemporalType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
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.LocalDateTime;
import java.time.LocalTime;
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 <T> the expected temporal type
*/
public class TemporalType<T extends Temporal> extends PropertyAndLeafType<T> {

/** Local Date temporal type. */
public static final TemporalType<LocalDate> 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<LocalTime> 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<LocalDateTime> 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<String> supportedFormats;
private final BiFunction<String, DateTimeFormatter, T> temporalParser;
private String defaultExportFormat;

/**
* 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<T> clazz, @NotNull List<String> supportedFormats,
@NotNull BiFunction<String, DateTimeFormatter, T> 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
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) {
return DateTimeFormatter.ofPattern(this.defaultExportFormat).format(value);
}

private T convertToTemporalType(String temporalText) {
for (String format: this.supportedFormats) {
try {
T parsedValue = this.temporalParser.apply(temporalText, DateTimeFormatter.ofPattern(format));
this.defaultExportFormat = format;
return parsedValue;
} catch (DateTimeParseException e) {
// try next format
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -59,7 +59,7 @@ void shouldReturnDefaultLeafTypes() {
List<MapperLeafType> 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));
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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());
}

Expand All @@ -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());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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<LocalDate> property = new LocalDateProperty("local-date.path.test", LocalDate.of(1970, 1, 31));

// when
PropertyValue<LocalDate> 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<LocalDate> property = new LocalDateProperty("local-date.path.wrong", defaultValue);

// when
PropertyValue<LocalDate> result = property.determineValue(reader);

// then
assertThat(result, isErrorValueOf(defaultValue));
}

@Test
void shouldReturnValueForExport() {
// given
Property<LocalDate> 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"));
}
}
Original file line number Diff line number Diff line change
@@ -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<LocalDateTime> property = new LocalDateTimeProperty("local-date-time.path.test", LocalDateTime.of(1970, 1, 31, 12, 0));

// when
PropertyValue<LocalDateTime> 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<LocalDateTime> property = new LocalDateTimeProperty("local-date-time.path.wrong", defaultDateTime);

// when
PropertyValue<LocalDateTime> result = property.determineValue(reader);

// then
assertThat(result, isErrorValueOf(defaultDateTime));
}

@Test
void shouldReturnValueForExport() {
// given
Property<LocalDateTime> 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"));
}
}
Loading