diff --git a/src/main/java/ch/jalu/configme/properties/OptionalProperty.java b/src/main/java/ch/jalu/configme/properties/OptionalProperty.java
index 55e6e32a..5b882c76 100644
--- a/src/main/java/ch/jalu/configme/properties/OptionalProperty.java
+++ b/src/main/java/ch/jalu/configme/properties/OptionalProperty.java
@@ -1,70 +1,50 @@
package ch.jalu.configme.properties;
-import ch.jalu.configme.properties.convertresult.PropertyValue;
-import ch.jalu.configme.resource.PropertyReader;
+import ch.jalu.configme.properties.types.OptionalPropertyType;
+import ch.jalu.configme.properties.types.PropertyType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
/**
- * Property which may be empty.
- *
- * Wraps another property with an {@link Optional}: if a property is not present in the property resource,
- * {@link Optional#empty} is returned.
+ * Optional property. Properties of this type may be absent from a property resource and it will still be considered
+ * valid.
*
* @param the type of value
*/
-public class OptionalProperty implements Property> {
-
- private final Property baseProperty;
- private final Optional defaultValue;
-
- public OptionalProperty(@NotNull Property baseProperty) {
- this.baseProperty = baseProperty;
- this.defaultValue = Optional.empty();
- }
-
- public OptionalProperty(@NotNull Property baseProperty, @NotNull T defaultValue) {
- this.baseProperty = baseProperty;
- this.defaultValue = Optional.of(defaultValue);
- }
-
- @Override
- public @NotNull String getPath() {
- return baseProperty.getPath();
- }
-
- @Override
- public @NotNull PropertyValue> determineValue(@NotNull PropertyReader reader) {
- PropertyValue basePropertyValue = baseProperty.determineValue(reader);
- Optional value = basePropertyValue.isValidInResource()
- ? Optional.ofNullable(basePropertyValue.getValue())
- : Optional.empty();
-
- // Propagate the false "valid" property if the reader has a value at the base property's path
- // and the base property says it's invalid -> triggers a rewrite to get rid of the invalid value.
- boolean isWrongInResource = !basePropertyValue.isValidInResource() && reader.contains(baseProperty.getPath());
- return isWrongInResource
- ? PropertyValue.withValueRequiringRewrite(value)
- : PropertyValue.withValidValue(value);
- }
-
- @Override
- public @NotNull Optional getDefaultValue() {
- return defaultValue;
+public class OptionalProperty extends TypeBasedProperty> {
+
+ /**
+ * Constructor. Creates a new property with an empty Optional as default value.
+ *
+ * @param path the path of the property
+ * @param valueType the property type of the value inside the optional
+ */
+ public OptionalProperty(@NotNull String path, @NotNull PropertyType valueType) {
+ this(path, new OptionalPropertyType<>(valueType), Optional.empty());
}
- @Override
- public boolean isValidValue(@Nullable Optional value) {
- if (value == null) {
- return false;
- }
- return value.map(baseProperty::isValidValue).orElse(true);
+ /**
+ * Constructor.
+ *
+ * @param path the path of the property
+ * @param valueType the property type of the value inside the optional
+ * @param defaultValue the default value of the property (will be wrapped in an Optional)
+ */
+ public OptionalProperty(@NotNull String path, @NotNull PropertyType valueType, @Nullable T defaultValue) {
+ this(path, new OptionalPropertyType<>(valueType), Optional.ofNullable(defaultValue));
}
- @Override
- public @Nullable Object toExportValue(@NotNull Optional value) {
- return value.map(baseProperty::toExportValue).orElse(null);
+ /**
+ * Constructor.
+ *
+ * @param path the path of the property
+ * @param type the type of this property
+ * @param defaultValue the default value of this property
+ */
+ public OptionalProperty(@NotNull String path, @NotNull PropertyType> type,
+ @NotNull Optional defaultValue) {
+ super(path, type, defaultValue);
}
}
diff --git a/src/main/java/ch/jalu/configme/properties/PropertyInitializer.java b/src/main/java/ch/jalu/configme/properties/PropertyInitializer.java
index 95afed5c..9b9e66f4 100644
--- a/src/main/java/ch/jalu/configme/properties/PropertyInitializer.java
+++ b/src/main/java/ch/jalu/configme/properties/PropertyInitializer.java
@@ -4,8 +4,15 @@
import ch.jalu.configme.properties.builder.CollectionPropertyBuilder;
import ch.jalu.configme.properties.builder.MapPropertyBuilder;
import ch.jalu.configme.properties.types.ArrayPropertyType;
+import ch.jalu.configme.properties.types.BooleanType;
+import ch.jalu.configme.properties.types.EnumPropertyType;
import ch.jalu.configme.properties.types.InlineArrayPropertyType;
+import ch.jalu.configme.properties.types.ListPropertyType;
+import ch.jalu.configme.properties.types.NumberType;
import ch.jalu.configme.properties.types.PropertyType;
+import ch.jalu.configme.properties.types.RegexType;
+import ch.jalu.configme.properties.types.SetPropertyType;
+import ch.jalu.configme.properties.types.StringType;
import org.jetbrains.annotations.NotNull;
import java.time.LocalDate;
@@ -311,48 +318,47 @@ public static ArrayPropertyBuilder> inlineArrayPro
// Optional flavors
// --------------
public static @NotNull OptionalProperty optionalBooleanProperty(@NotNull String path) {
- return new OptionalProperty<>(new BooleanProperty(path, false));
+ return new OptionalProperty<>(path, BooleanType.BOOLEAN);
}
public static @NotNull OptionalProperty optionalShortProperty(@NotNull String path) {
- return new OptionalProperty<>(new ShortProperty(path, (short) 0));
+ return new OptionalProperty<>(path, NumberType.SHORT);
}
public static @NotNull OptionalProperty optionalIntegerProperty(@NotNull String path) {
- return new OptionalProperty<>(new IntegerProperty(path, 0));
+ return new OptionalProperty<>(path, NumberType.INTEGER);
}
public static @NotNull OptionalProperty optionalLongProperty(@NotNull String path) {
- return new OptionalProperty<>(new LongProperty(path, 0L));
+ return new OptionalProperty<>(path, NumberType.LONG);
}
public static @NotNull OptionalProperty optionalFloatProperty(@NotNull String path) {
- return new OptionalProperty<>(new FloatProperty(path, 0f));
+ return new OptionalProperty<>(path, NumberType.FLOAT);
}
public static @NotNull OptionalProperty optionalDoubleProperty(@NotNull String path) {
- return new OptionalProperty<>(new DoubleProperty(path, 0.0));
+ return new OptionalProperty<>(path, NumberType.DOUBLE);
}
public static @NotNull OptionalProperty optionalStringProperty(@NotNull String path) {
- return new OptionalProperty<>(new StringProperty(path, ""));
+ return new OptionalProperty<>(path, StringType.STRING);
}
public static > @NotNull OptionalProperty optionalEnumProperty(@NotNull Class clazz,
@NotNull String path) {
- // default value may never be null, so get the first entry in the enum class
- return new OptionalProperty<>(new EnumProperty<>(path, clazz, clazz.getEnumConstants()[0]));
+ return new OptionalProperty<>(path, new EnumPropertyType<>(clazz));
}
public static @NotNull OptionalProperty optionalRegexProperty(@NotNull String path) {
- return new OptionalProperty<>(new RegexProperty(path, ""));
+ return new OptionalProperty<>(path, RegexType.REGEX);
}
public static @NotNull OptionalProperty> optionalListProperty(@NotNull String path) {
- return new OptionalProperty<>(new StringListProperty(path));
+ return new OptionalProperty<>(path, new ListPropertyType<>(StringType.STRING));
}
public static @NotNull OptionalProperty> optionalSetProperty(@NotNull String path) {
- return new OptionalProperty<>(new StringSetProperty(path));
+ return new OptionalProperty<>(path, new SetPropertyType<>(StringType.STRING));
}
}
diff --git a/src/main/java/ch/jalu/configme/properties/types/OptionalPropertyType.java b/src/main/java/ch/jalu/configme/properties/types/OptionalPropertyType.java
new file mode 100644
index 00000000..a8e78720
--- /dev/null
+++ b/src/main/java/ch/jalu/configme/properties/types/OptionalPropertyType.java
@@ -0,0 +1,39 @@
+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.util.Optional;
+
+/**
+ * Property type for optionals. Wraps another property type.
+ *
+ * @param the value type of the optional
+ */
+public class OptionalPropertyType implements PropertyType> {
+
+ private final PropertyType valueType;
+
+ /**
+ * Constructor.
+ *
+ * @param valueType the property type to handle the value inside the optional
+ */
+ public OptionalPropertyType(PropertyType valueType) {
+ this.valueType = valueType;
+ }
+
+ @Override
+ public @NotNull Optional convert(@Nullable Object object, @NotNull ConvertErrorRecorder errorRecorder) {
+ if (object != null) {
+ return Optional.ofNullable(valueType.convert(object, errorRecorder));
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public @Nullable Object toExportValue(@NotNull Optional value) {
+ return value.map(valueType::toExportValue).orElse(null);
+ }
+}
diff --git a/src/test/java/ch/jalu/configme/SettingsManagerImplTest.java b/src/test/java/ch/jalu/configme/SettingsManagerImplTest.java
index 64b5f900..53967f06 100644
--- a/src/test/java/ch/jalu/configme/SettingsManagerImplTest.java
+++ b/src/test/java/ch/jalu/configme/SettingsManagerImplTest.java
@@ -9,6 +9,7 @@
import ch.jalu.configme.properties.BeanProperty;
import ch.jalu.configme.properties.OptionalProperty;
import ch.jalu.configme.properties.Property;
+import ch.jalu.configme.properties.types.NumberType;
import ch.jalu.configme.resource.PropertyReader;
import ch.jalu.configme.resource.PropertyResource;
import ch.jalu.configme.resource.YamlFileResource;
@@ -203,7 +204,7 @@ void shouldSetOptionalPropertyCorrectly() {
PropertyResource resource = new YamlFileResource(file);
SettingsManager settingsManager =
new SettingsManagerImpl(resource, createConfiguration(TestConfiguration.class), null);
- OptionalProperty intOptional = new OptionalProperty<>(newProperty("version", 65));
+ OptionalProperty intOptional = new OptionalProperty<>("version", NumberType.INTEGER);
// when
settingsManager.setProperty(intOptional, Optional.empty());
diff --git a/src/test/java/ch/jalu/configme/properties/OptionalPropertyTest.java b/src/test/java/ch/jalu/configme/properties/OptionalPropertyTest.java
index f7e19055..175bf3ba 100644
--- a/src/test/java/ch/jalu/configme/properties/OptionalPropertyTest.java
+++ b/src/test/java/ch/jalu/configme/properties/OptionalPropertyTest.java
@@ -1,6 +1,11 @@
package ch.jalu.configme.properties;
import ch.jalu.configme.properties.convertresult.PropertyValue;
+import ch.jalu.configme.properties.types.BooleanType;
+import ch.jalu.configme.properties.types.EnumPropertyType;
+import ch.jalu.configme.properties.types.NumberType;
+import ch.jalu.configme.properties.types.PropertyType;
+import ch.jalu.configme.properties.types.StringType;
import ch.jalu.configme.resource.PropertyReader;
import ch.jalu.configme.samples.TestEnum;
import org.junit.jupiter.api.Test;
@@ -9,17 +14,16 @@
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
import static ch.jalu.configme.TestUtils.isErrorValueOf;
import static ch.jalu.configme.TestUtils.isValidValueOf;
import static java.util.Optional.of;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.nullValue;
import static org.mockito.BDDMockito.given;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.only;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.mock;
/**
* Test for {@link OptionalProperty}.
@@ -33,9 +37,9 @@ class OptionalPropertyTest {
@Test
void shouldReturnPresentValues() {
// given
- OptionalProperty booleanProp = new OptionalProperty<>(new BooleanProperty("bool.path.test", false));
- OptionalProperty intProp = new OptionalProperty<>(new IntegerProperty("int.path.test", 0));
- OptionalProperty enumProp = new OptionalProperty<>(new EnumProperty<>("enum.path.test", TestEnum.class, TestEnum.SECOND));
+ OptionalProperty booleanProp = new OptionalProperty<>("bool.path.test", BooleanType.BOOLEAN);
+ OptionalProperty intProp = new OptionalProperty<>("int.path.test", NumberType.INTEGER);
+ OptionalProperty enumProp = new OptionalProperty<>("enum.path.test", new EnumPropertyType<>(TestEnum.class));
given(reader.getObject("bool.path.test")).willReturn(true);
given(reader.getObject("int.path.test")).willReturn(27);
@@ -55,9 +59,9 @@ void shouldReturnPresentValues() {
@Test
void shouldReturnEmptyOptional() {
// given
- OptionalProperty booleanProp = new OptionalProperty<>(new BooleanProperty("bool.path.wrong", false));
- OptionalProperty intProp = new OptionalProperty<>(new IntegerProperty("int.path.wrong", 0));
- OptionalProperty enumProp = new OptionalProperty<>(new EnumProperty<>("enum.path.wrong", TestEnum.class, TestEnum.SECOND));
+ OptionalProperty booleanProp = new OptionalProperty<>("bool.path.wrong", BooleanType.BOOLEAN);
+ OptionalProperty intProp = new OptionalProperty<>("int.path.wrong", NumberType.INTEGER);
+ OptionalProperty enumProp = new OptionalProperty<>("enum.path.wrong", new EnumPropertyType<>(TestEnum.class));
// when
PropertyValue> boolResult = booleanProp.determineValue(reader);
@@ -73,7 +77,7 @@ void shouldReturnEmptyOptional() {
@Test
void shouldAllowToDefineDefaultValue() {
// given
- OptionalProperty integerProp = new OptionalProperty<>(new IntegerProperty("path", 0), 42);
+ OptionalProperty integerProp = new OptionalProperty<>("int.path.wrong", NumberType.INTEGER, 42);
// when
Optional defaultValue = integerProp.getDefaultValue();
@@ -85,48 +89,67 @@ void shouldAllowToDefineDefaultValue() {
@Test
void shouldReturnValueWithInvalidFlagIfReturnedFromReader() {
// given
- StringProperty baseProperty = spy(new StringProperty("the.path", "DEFAULT"));
- doReturn(PropertyValue.withValueRequiringRewrite("this should be discarded")).when(baseProperty).determineValue(reader);
- given(reader.contains("the.path")).willReturn(true);
- OptionalProperty optionalProperty = new OptionalProperty<>(baseProperty);
+ given(reader.getObject("the.path")).willReturn(400);
+ OptionalProperty optionalProperty = new OptionalProperty<>("the.path", NumberType.BYTE);
// when
- PropertyValue> value = optionalProperty.determineValue(reader);
+ PropertyValue> value = optionalProperty.determineValue(reader);
// then
- assertThat(value, isErrorValueOf(Optional.empty()));
+ assertThat(value, isErrorValueOf(Optional.of(Byte.MAX_VALUE)));
}
@Test
- void shouldDelegateToBasePropertyAndHaveEmptyOptionalAsDefault() {
+ void shouldValidateWithBasePropertyNullSafe() {
// given
- StringProperty baseProperty = new StringProperty("some.path", "Def");
- OptionalProperty property = new OptionalProperty<>(baseProperty);
+ OptionalProperty property = new OptionalProperty<>("path", StringType.STRING);
// when
- Optional defaultValue = property.getDefaultValue();
- String path = property.getPath();
+ boolean isEmptyValid = property.isValidValue(Optional.empty());
+ boolean isValueValid = property.isValidValue(Optional.of("foo"));
+ boolean isNullValid = property.isValidValue(null);
// then
- assertThat(defaultValue, equalTo(Optional.empty()));
- assertThat(path, equalTo("some.path"));
+ assertThat(isEmptyValid, equalTo(true));
+ assertThat(isValueValid, equalTo(true));
+ assertThat(isNullValid, equalTo(false));
}
@Test
- void shouldValidateWithBasePropertyNullSafe() {
+ void shouldReturnNullAsExportValue() {
// given
- StringProperty baseProperty = spy(new StringProperty("some.path", "Def"));
- OptionalProperty property = new OptionalProperty<>(baseProperty);
+ OptionalProperty property = new OptionalProperty<>("int.path", NumberType.INTEGER);
// when
- boolean isEmptyValid = property.isValidValue(Optional.empty());
- boolean isValueValid = property.isValidValue(Optional.of("foo"));
- boolean isNullValid = property.isValidValue(null);
+ Object exportValue = property.toExportValue(Optional.empty());
// then
- assertThat(isEmptyValid, equalTo(true));
- assertThat(isValueValid, equalTo(true));
- assertThat(isNullValid, equalTo(false));
- verify(baseProperty, only()).isValidValue("foo");
+ assertThat(exportValue, nullValue());
+ }
+
+ @Test
+ void shouldReturnNullIfValuePropertyTypeReturnsNull() {
+ // given
+ PropertyType valuePropertyType = mock(PropertyType.class);
+ given(valuePropertyType.toExportValue("demo")).willReturn(null);
+ OptionalProperty optionalProperty = new OptionalProperty<>("int.path", valuePropertyType);
+
+ // when
+ Object exportValue = optionalProperty.toExportValue(Optional.of("demo"));
+
+ // then
+ assertThat(exportValue, nullValue());
+ }
+
+ @Test
+ void shouldConstructExportValue() {
+ // given
+ OptionalProperty optionalProperty = new OptionalProperty<>("duration.unit", EnumPropertyType.of(TimeUnit.class));
+
+ // when
+ Object exportValue = optionalProperty.toExportValue(Optional.of(TimeUnit.HOURS));
+
+ // then
+ assertThat(exportValue, equalTo("HOURS"));
}
}
diff --git a/src/test/java/ch/jalu/configme/resource/UniqueCommentTest.java b/src/test/java/ch/jalu/configme/resource/UniqueCommentTest.java
index 743673ed..fb55a6ca 100644
--- a/src/test/java/ch/jalu/configme/resource/UniqueCommentTest.java
+++ b/src/test/java/ch/jalu/configme/resource/UniqueCommentTest.java
@@ -5,7 +5,6 @@
import ch.jalu.configme.TestUtils;
import ch.jalu.configme.configurationdata.ConfigurationData;
import ch.jalu.configme.configurationdata.ConfigurationDataBuilder;
-import ch.jalu.configme.properties.BeanProperty;
import ch.jalu.configme.properties.ListProperty;
import ch.jalu.configme.properties.MapProperty;
import ch.jalu.configme.properties.OptionalProperty;
@@ -141,7 +140,7 @@ public static final class ServerSettingHolder implements SettingsHolder {
new ServerCollection(true, "reception"), new ServerCollection(false, "lobby"));
public static final Property> ALT =
- new OptionalProperty<>(new BeanProperty<>("alternative", ServerCollection.class, new ServerCollection()));
+ new OptionalProperty<>("alternative", BeanPropertyType.of(ServerCollection.class));
private ServerSettingHolder() {
}
diff --git a/src/test/java/ch/jalu/configme/resource/YamlFileResourceTest.java b/src/test/java/ch/jalu/configme/resource/YamlFileResourceTest.java
index c37c7fb5..d825c16c 100644
--- a/src/test/java/ch/jalu/configme/resource/YamlFileResourceTest.java
+++ b/src/test/java/ch/jalu/configme/resource/YamlFileResourceTest.java
@@ -8,6 +8,8 @@
import ch.jalu.configme.properties.BeanProperty;
import ch.jalu.configme.properties.OptionalProperty;
import ch.jalu.configme.properties.Property;
+import ch.jalu.configme.properties.types.EnumPropertyType;
+import ch.jalu.configme.properties.types.NumberType;
import ch.jalu.configme.samples.TestConfiguration;
import ch.jalu.configme.samples.TestEnum;
import org.junit.jupiter.api.Test;
@@ -195,8 +197,8 @@ void shouldExportConfigurationWithExpectedComments() throws IOException {
void shouldSkipAbsentOptionalProperty() throws IOException {
// given
ConfigurationData configurationData = createConfiguration(asList(
- new OptionalProperty<>(TestConfiguration.DURATION_IN_SECONDS),
- new OptionalProperty<>(TestConfiguration.RATIO_ORDER)));
+ new OptionalProperty<>("test.duration", NumberType.INTEGER),
+ new OptionalProperty<>("sample.ratio.order", new EnumPropertyType<>(TestEnum.class))));
Path file = copyFileFromResources(INCOMPLETE_FILE);
PropertyResource resource = new YamlFileResource(file);
configurationData.initializeValues(resource.createReader());
@@ -216,8 +218,8 @@ void shouldSkipAbsentOptionalProperty() throws IOException {
void shouldExportAllPresentOptionalProperties() throws IOException {
// given
ConfigurationData configurationData = createConfiguration(asList(
- new OptionalProperty<>(TestConfiguration.DURATION_IN_SECONDS),
- new OptionalProperty<>(TestConfiguration.RATIO_ORDER)));
+ new OptionalProperty<>("test.duration", NumberType.INTEGER),
+ new OptionalProperty<>("sample.ratio.order", new EnumPropertyType<>(TestEnum.class))));
Path file = copyFileFromResources(COMPLETE_FILE);
PropertyResource resource = new YamlFileResource(file);
configurationData.initializeValues(resource.createReader());