Skip to content

Commit 811d120

Browse files
committed
#448 Change OptionalProperty to be based on a property type
1 parent 2ccc783 commit 811d120

7 files changed

Lines changed: 115 additions & 107 deletions

File tree

Lines changed: 33 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,50 @@
11
package ch.jalu.configme.properties;
22

3-
import ch.jalu.configme.properties.convertresult.PropertyValue;
4-
import ch.jalu.configme.resource.PropertyReader;
3+
import ch.jalu.configme.properties.types.OptionalPropertyType;
4+
import ch.jalu.configme.properties.types.PropertyType;
55
import org.jetbrains.annotations.NotNull;
66
import org.jetbrains.annotations.Nullable;
77

88
import java.util.Optional;
99

1010
/**
11-
* Property which may be empty.
12-
* <p>
13-
* Wraps another property with an {@link Optional}: if a property is not present in the property resource,
14-
* {@link Optional#empty} is returned.
11+
* Optional property. Properties of this type may be absent from a property resource and it will still be considered
12+
* valid.
1513
*
1614
* @param <T> the type of value
1715
*/
18-
public class OptionalProperty<T> implements Property<Optional<T>> {
19-
20-
private final Property<T> baseProperty;
21-
private final Optional<T> defaultValue;
22-
23-
public OptionalProperty(@NotNull Property<T> baseProperty) {
24-
this.baseProperty = baseProperty;
25-
this.defaultValue = Optional.empty();
26-
}
27-
28-
public OptionalProperty(@NotNull Property<T> baseProperty, @NotNull T defaultValue) {
29-
this.baseProperty = baseProperty;
30-
this.defaultValue = Optional.of(defaultValue);
31-
}
32-
33-
@Override
34-
public @NotNull String getPath() {
35-
return baseProperty.getPath();
36-
}
37-
38-
@Override
39-
public @NotNull PropertyValue<Optional<T>> determineValue(@NotNull PropertyReader reader) {
40-
PropertyValue<T> basePropertyValue = baseProperty.determineValue(reader);
41-
Optional<T> value = basePropertyValue.isValidInResource()
42-
? Optional.ofNullable(basePropertyValue.getValue())
43-
: Optional.empty();
44-
45-
// Propagate the false "valid" property if the reader has a value at the base property's path
46-
// and the base property says it's invalid -> triggers a rewrite to get rid of the invalid value.
47-
boolean isWrongInResource = !basePropertyValue.isValidInResource() && reader.contains(baseProperty.getPath());
48-
return isWrongInResource
49-
? PropertyValue.withValueRequiringRewrite(value)
50-
: PropertyValue.withValidValue(value);
51-
}
52-
53-
@Override
54-
public @NotNull Optional<T> getDefaultValue() {
55-
return defaultValue;
16+
public class OptionalProperty<T> extends TypeBasedProperty<Optional<T>> {
17+
18+
/**
19+
* Constructor. Creates a new property with an empty Optional as default value.
20+
*
21+
* @param path the path of the property
22+
* @param valueType the property type of the value inside the optional
23+
*/
24+
public OptionalProperty(@NotNull String path, @NotNull PropertyType<T> valueType) {
25+
this(path, new OptionalPropertyType<>(valueType), Optional.empty());
5626
}
5727

58-
@Override
59-
public boolean isValidValue(@Nullable Optional<T> value) {
60-
if (value == null) {
61-
return false;
62-
}
63-
return value.map(baseProperty::isValidValue).orElse(true);
28+
/**
29+
* Constructor.
30+
*
31+
* @param path the path of the property
32+
* @param valueType the property type of the value inside the optional
33+
* @param defaultValue the default value of the property (will be wrapped in an Optional)
34+
*/
35+
public OptionalProperty(@NotNull String path, @NotNull PropertyType<T> valueType, @Nullable T defaultValue) {
36+
this(path, new OptionalPropertyType<>(valueType), Optional.ofNullable(defaultValue));
6437
}
6538

66-
@Override
67-
public @Nullable Object toExportValue(@NotNull Optional<T> value) {
68-
return value.map(baseProperty::toExportValue).orElse(null);
39+
/**
40+
* Constructor.
41+
*
42+
* @param path the path of the property
43+
* @param type the type of this property
44+
* @param defaultValue the default value of this property
45+
*/
46+
public OptionalProperty(@NotNull String path, @NotNull PropertyType<Optional<T>> type,
47+
@NotNull Optional<T> defaultValue) {
48+
super(path, type, defaultValue);
6949
}
7050
}

src/main/java/ch/jalu/configme/properties/PropertyInitializer.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@
44
import ch.jalu.configme.properties.builder.CollectionPropertyBuilder;
55
import ch.jalu.configme.properties.builder.MapPropertyBuilder;
66
import ch.jalu.configme.properties.types.ArrayPropertyType;
7+
import ch.jalu.configme.properties.types.BooleanType;
8+
import ch.jalu.configme.properties.types.EnumPropertyType;
79
import ch.jalu.configme.properties.types.InlineArrayPropertyType;
10+
import ch.jalu.configme.properties.types.ListPropertyType;
11+
import ch.jalu.configme.properties.types.NumberType;
812
import ch.jalu.configme.properties.types.PropertyType;
13+
import ch.jalu.configme.properties.types.RegexType;
14+
import ch.jalu.configme.properties.types.SetPropertyType;
15+
import ch.jalu.configme.properties.types.StringType;
916
import org.jetbrains.annotations.NotNull;
1017

1118
import java.util.Collection;
@@ -272,48 +279,47 @@ public static <E> ArrayPropertyBuilder<E, InlineArrayProperty<E>> inlineArrayPro
272279
// Optional flavors
273280
// --------------
274281
public static @NotNull OptionalProperty<Boolean> optionalBooleanProperty(@NotNull String path) {
275-
return new OptionalProperty<>(new BooleanProperty(path, false));
282+
return new OptionalProperty<>(path, BooleanType.BOOLEAN);
276283
}
277284

278285
public static @NotNull OptionalProperty<Short> optionalShortProperty(@NotNull String path) {
279-
return new OptionalProperty<>(new ShortProperty(path, (short) 0));
286+
return new OptionalProperty<>(path, NumberType.SHORT);
280287
}
281288

282289
public static @NotNull OptionalProperty<Integer> optionalIntegerProperty(@NotNull String path) {
283-
return new OptionalProperty<>(new IntegerProperty(path, 0));
290+
return new OptionalProperty<>(path, NumberType.INTEGER);
284291
}
285292

286293
public static @NotNull OptionalProperty<Long> optionalLongProperty(@NotNull String path) {
287-
return new OptionalProperty<>(new LongProperty(path, 0L));
294+
return new OptionalProperty<>(path, NumberType.LONG);
288295
}
289296

290297
public static @NotNull OptionalProperty<Float> optionalFloatProperty(@NotNull String path) {
291-
return new OptionalProperty<>(new FloatProperty(path, 0f));
298+
return new OptionalProperty<>(path, NumberType.FLOAT);
292299
}
293300

294301
public static @NotNull OptionalProperty<Double> optionalDoubleProperty(@NotNull String path) {
295-
return new OptionalProperty<>(new DoubleProperty(path, 0.0));
302+
return new OptionalProperty<>(path, NumberType.DOUBLE);
296303
}
297304

298305
public static @NotNull OptionalProperty<String> optionalStringProperty(@NotNull String path) {
299-
return new OptionalProperty<>(new StringProperty(path, ""));
306+
return new OptionalProperty<>(path, StringType.STRING);
300307
}
301308

302309
public static <E extends Enum<E>> @NotNull OptionalProperty<E> optionalEnumProperty(@NotNull Class<E> clazz,
303310
@NotNull String path) {
304-
// default value may never be null, so get the first entry in the enum class
305-
return new OptionalProperty<>(new EnumProperty<>(path, clazz, clazz.getEnumConstants()[0]));
311+
return new OptionalProperty<>(path, new EnumPropertyType<>(clazz));
306312
}
307313

308314
public static @NotNull OptionalProperty<Pattern> optionalRegexProperty(@NotNull String path) {
309-
return new OptionalProperty<>(new RegexProperty(path, ""));
315+
return new OptionalProperty<>(path, RegexType.REGEX);
310316
}
311317

312318
public static @NotNull OptionalProperty<List<String>> optionalListProperty(@NotNull String path) {
313-
return new OptionalProperty<>(new StringListProperty(path));
319+
return new OptionalProperty<>(path, new ListPropertyType<>(StringType.STRING));
314320
}
315321

316322
public static @NotNull OptionalProperty<Set<String>> optionalSetProperty(@NotNull String path) {
317-
return new OptionalProperty<>(new StringSetProperty(path));
323+
return new OptionalProperty<>(path, new SetPropertyType<>(StringType.STRING));
318324
}
319325
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package ch.jalu.configme.properties.types;
2+
3+
import ch.jalu.configme.properties.convertresult.ConvertErrorRecorder;
4+
import org.jetbrains.annotations.NotNull;
5+
import org.jetbrains.annotations.Nullable;
6+
7+
import java.util.Optional;
8+
9+
/**
10+
* Property type for optionals. Wraps another property type.
11+
*
12+
* @param <T> the value type of the optional
13+
*/
14+
public class OptionalPropertyType<T> implements PropertyType<Optional<T>> {
15+
16+
private final PropertyType<T> valueType;
17+
18+
/**
19+
* Constructor.
20+
*
21+
* @param valueType the property type to handle the value inside the optional
22+
*/
23+
public OptionalPropertyType(PropertyType<T> valueType) {
24+
this.valueType = valueType;
25+
}
26+
27+
@Override
28+
public @Nullable Optional<T> convert(@Nullable Object object, @NotNull ConvertErrorRecorder errorRecorder) {
29+
if (object != null) {
30+
return Optional.ofNullable(valueType.convert(object, errorRecorder));
31+
}
32+
return Optional.empty();
33+
}
34+
35+
@Override
36+
public @Nullable Object toExportValue(@NotNull Optional<T> value) {
37+
return value.map(valueType::toExportValue).orElse(null);
38+
}
39+
}

src/test/java/ch/jalu/configme/SettingsManagerImplTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import ch.jalu.configme.properties.BeanProperty;
1010
import ch.jalu.configme.properties.OptionalProperty;
1111
import ch.jalu.configme.properties.Property;
12+
import ch.jalu.configme.properties.types.NumberType;
1213
import ch.jalu.configme.resource.PropertyReader;
1314
import ch.jalu.configme.resource.PropertyResource;
1415
import ch.jalu.configme.resource.YamlFileResource;
@@ -203,7 +204,7 @@ void shouldSetOptionalPropertyCorrectly() {
203204
PropertyResource resource = new YamlFileResource(file);
204205
SettingsManager settingsManager =
205206
new SettingsManagerImpl(resource, createConfiguration(TestConfiguration.class), null);
206-
OptionalProperty<Integer> intOptional = new OptionalProperty<>(newProperty("version", 65));
207+
OptionalProperty<Integer> intOptional = new OptionalProperty<>("version", NumberType.INTEGER);
207208

208209
// when
209210
settingsManager.setProperty(intOptional, Optional.empty());

src/test/java/ch/jalu/configme/properties/OptionalPropertyTest.java

Lines changed: 16 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package ch.jalu.configme.properties;
22

33
import ch.jalu.configme.properties.convertresult.PropertyValue;
4+
import ch.jalu.configme.properties.types.BooleanType;
5+
import ch.jalu.configme.properties.types.EnumPropertyType;
6+
import ch.jalu.configme.properties.types.NumberType;
7+
import ch.jalu.configme.properties.types.StringType;
48
import ch.jalu.configme.resource.PropertyReader;
59
import ch.jalu.configme.samples.TestEnum;
610
import org.junit.jupiter.api.Test;
@@ -16,10 +20,6 @@
1620
import static org.hamcrest.MatcherAssert.assertThat;
1721
import static org.hamcrest.Matchers.equalTo;
1822
import static org.mockito.BDDMockito.given;
19-
import static org.mockito.Mockito.doReturn;
20-
import static org.mockito.Mockito.only;
21-
import static org.mockito.Mockito.spy;
22-
import static org.mockito.Mockito.verify;
2323

2424
/**
2525
* Test for {@link OptionalProperty}.
@@ -33,9 +33,9 @@ class OptionalPropertyTest {
3333
@Test
3434
void shouldReturnPresentValues() {
3535
// given
36-
OptionalProperty<Boolean> booleanProp = new OptionalProperty<>(new BooleanProperty("bool.path.test", false));
37-
OptionalProperty<Integer> intProp = new OptionalProperty<>(new IntegerProperty("int.path.test", 0));
38-
OptionalProperty<TestEnum> enumProp = new OptionalProperty<>(new EnumProperty<>("enum.path.test", TestEnum.class, TestEnum.SECOND));
36+
OptionalProperty<Boolean> booleanProp = new OptionalProperty<>("bool.path.test", BooleanType.BOOLEAN);
37+
OptionalProperty<Integer> intProp = new OptionalProperty<>("int.path.test", NumberType.INTEGER);
38+
OptionalProperty<TestEnum> enumProp = new OptionalProperty<>("enum.path.test", new EnumPropertyType<>(TestEnum.class));
3939

4040
given(reader.getObject("bool.path.test")).willReturn(true);
4141
given(reader.getObject("int.path.test")).willReturn(27);
@@ -55,9 +55,9 @@ void shouldReturnPresentValues() {
5555
@Test
5656
void shouldReturnEmptyOptional() {
5757
// given
58-
OptionalProperty<Boolean> booleanProp = new OptionalProperty<>(new BooleanProperty("bool.path.wrong", false));
59-
OptionalProperty<Integer> intProp = new OptionalProperty<>(new IntegerProperty("int.path.wrong", 0));
60-
OptionalProperty<TestEnum> enumProp = new OptionalProperty<>(new EnumProperty<>("enum.path.wrong", TestEnum.class, TestEnum.SECOND));
58+
OptionalProperty<Boolean> booleanProp = new OptionalProperty<>("bool.path.wrong", BooleanType.BOOLEAN);
59+
OptionalProperty<Integer> intProp = new OptionalProperty<>("int.path.wrong", NumberType.INTEGER);
60+
OptionalProperty<TestEnum> enumProp = new OptionalProperty<>("enum.path.wrong", new EnumPropertyType<>(TestEnum.class));
6161

6262
// when
6363
PropertyValue<Optional<Boolean>> boolResult = booleanProp.determineValue(reader);
@@ -73,7 +73,7 @@ void shouldReturnEmptyOptional() {
7373
@Test
7474
void shouldAllowToDefineDefaultValue() {
7575
// given
76-
OptionalProperty<Integer> integerProp = new OptionalProperty<>(new IntegerProperty("path", 0), 42);
76+
OptionalProperty<Integer> integerProp = new OptionalProperty<>("int.path.wrong", NumberType.INTEGER, 42);
7777

7878
// when
7979
Optional<Integer> defaultValue = integerProp.getDefaultValue();
@@ -85,38 +85,20 @@ void shouldAllowToDefineDefaultValue() {
8585
@Test
8686
void shouldReturnValueWithInvalidFlagIfReturnedFromReader() {
8787
// given
88-
StringProperty baseProperty = spy(new StringProperty("the.path", "DEFAULT"));
89-
doReturn(PropertyValue.withValueRequiringRewrite("this should be discarded")).when(baseProperty).determineValue(reader);
90-
given(reader.contains("the.path")).willReturn(true);
91-
OptionalProperty<String> optionalProperty = new OptionalProperty<>(baseProperty);
88+
given(reader.getObject("the.path")).willReturn(400);
89+
OptionalProperty<Byte> optionalProperty = new OptionalProperty<>("the.path", NumberType.BYTE);
9290

9391
// when
94-
PropertyValue<Optional<String>> value = optionalProperty.determineValue(reader);
92+
PropertyValue<Optional<Byte>> value = optionalProperty.determineValue(reader);
9593

9694
// then
97-
assertThat(value, isErrorValueOf(Optional.empty()));
98-
}
99-
100-
@Test
101-
void shouldDelegateToBasePropertyAndHaveEmptyOptionalAsDefault() {
102-
// given
103-
StringProperty baseProperty = new StringProperty("some.path", "Def");
104-
OptionalProperty<String> property = new OptionalProperty<>(baseProperty);
105-
106-
// when
107-
Optional<String> defaultValue = property.getDefaultValue();
108-
String path = property.getPath();
109-
110-
// then
111-
assertThat(defaultValue, equalTo(Optional.empty()));
112-
assertThat(path, equalTo("some.path"));
95+
assertThat(value, isErrorValueOf(Optional.of(Byte.MAX_VALUE)));
11396
}
11497

11598
@Test
11699
void shouldValidateWithBasePropertyNullSafe() {
117100
// given
118-
StringProperty baseProperty = spy(new StringProperty("some.path", "Def"));
119-
OptionalProperty<String> property = new OptionalProperty<>(baseProperty);
101+
OptionalProperty<String> property = new OptionalProperty<>("path", StringType.STRING);
120102

121103
// when
122104
boolean isEmptyValid = property.isValidValue(Optional.empty());
@@ -127,6 +109,5 @@ void shouldValidateWithBasePropertyNullSafe() {
127109
assertThat(isEmptyValid, equalTo(true));
128110
assertThat(isValueValid, equalTo(true));
129111
assertThat(isNullValid, equalTo(false));
130-
verify(baseProperty, only()).isValidValue("foo");
131112
}
132113
}

src/test/java/ch/jalu/configme/resource/UniqueCommentTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import ch.jalu.configme.TestUtils;
66
import ch.jalu.configme.configurationdata.ConfigurationData;
77
import ch.jalu.configme.configurationdata.ConfigurationDataBuilder;
8-
import ch.jalu.configme.properties.BeanProperty;
98
import ch.jalu.configme.properties.ListProperty;
109
import ch.jalu.configme.properties.MapProperty;
1110
import ch.jalu.configme.properties.OptionalProperty;
@@ -141,7 +140,7 @@ public static final class ServerSettingHolder implements SettingsHolder {
141140
new ServerCollection(true, "reception"), new ServerCollection(false, "lobby"));
142141

143142
public static final Property<Optional<ServerCollection>> ALT =
144-
new OptionalProperty<>(new BeanProperty<>("alternative", ServerCollection.class, new ServerCollection()));
143+
new OptionalProperty<>("alternative", BeanPropertyType.of(ServerCollection.class));
145144

146145
private ServerSettingHolder() {
147146
}

src/test/java/ch/jalu/configme/resource/YamlFileResourceTest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import ch.jalu.configme.properties.BeanProperty;
99
import ch.jalu.configme.properties.OptionalProperty;
1010
import ch.jalu.configme.properties.Property;
11+
import ch.jalu.configme.properties.types.EnumPropertyType;
12+
import ch.jalu.configme.properties.types.NumberType;
1113
import ch.jalu.configme.samples.TestConfiguration;
1214
import ch.jalu.configme.samples.TestEnum;
1315
import org.junit.jupiter.api.Test;
@@ -195,8 +197,8 @@ void shouldExportConfigurationWithExpectedComments() throws IOException {
195197
void shouldSkipAbsentOptionalProperty() throws IOException {
196198
// given
197199
ConfigurationData configurationData = createConfiguration(asList(
198-
new OptionalProperty<>(TestConfiguration.DURATION_IN_SECONDS),
199-
new OptionalProperty<>(TestConfiguration.RATIO_ORDER)));
200+
new OptionalProperty<>("test.duration", NumberType.INTEGER),
201+
new OptionalProperty<>("sample.ratio.order", new EnumPropertyType<>(TestEnum.class))));
200202
Path file = copyFileFromResources(INCOMPLETE_FILE);
201203
PropertyResource resource = new YamlFileResource(file);
202204
configurationData.initializeValues(resource.createReader());
@@ -216,8 +218,8 @@ void shouldSkipAbsentOptionalProperty() throws IOException {
216218
void shouldExportAllPresentOptionalProperties() throws IOException {
217219
// given
218220
ConfigurationData configurationData = createConfiguration(asList(
219-
new OptionalProperty<>(TestConfiguration.DURATION_IN_SECONDS),
220-
new OptionalProperty<>(TestConfiguration.RATIO_ORDER)));
221+
new OptionalProperty<>("test.duration", NumberType.INTEGER),
222+
new OptionalProperty<>("sample.ratio.order", new EnumPropertyType<>(TestEnum.class))));
221223
Path file = copyFileFromResources(COMPLETE_FILE);
222224
PropertyResource resource = new YamlFileResource(file);
223225
configurationData.initializeValues(resource.createReader());

0 commit comments

Comments
 (0)