Skip to content

Commit f33e7f6

Browse files
Daniel Kraschewskihankem
authored andcommitted
string-to-enum-conversion now works just by default
Signed-off-by: Daniel Kraschewski <[email protected]>
1 parent 5a91596 commit f33e7f6

File tree

8 files changed

+156
-27
lines changed

8 files changed

+156
-27
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ This order can be customized, see [§5 (Change the order in which annotations ar
9494
Fields don't have to be Strings. You can configure collection fields or even any type you wish (or a collection of that type).
9595

9696
Some simple transformers are included and used by default, e.g. a String will automatically be converted to an integer, a
97-
boolean value or even a collection as needed.
97+
boolean value, an enum value, or even a collection as needed.
9898

9999
If you need more complex transformers, you can also implement your own by extending the `TypeTransformer` class, and specifying them in the `@TypeTransformers` annotation.
100100

src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToEnumTypeTransformer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ public StringToEnumTypeTransformer(Class<E> enumClass) {
88
this.enumClass = enumClass;
99
}
1010

11+
@Override
12+
public boolean isMatching(Class<?> sourceClass, Class<?> targetClass) {
13+
return sourceClass.equals(String.class) && targetClass.equals(enumClass);
14+
}
15+
1116
@Override
1217
public E transform(final String value) {
1318
return E.valueOf(enumClass, value.trim().replace(' ', '_').toUpperCase());
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.tngtech.configbuilder.util;
2+
3+
import java.lang.reflect.ParameterizedType;
4+
import java.lang.reflect.Type;
5+
import java.util.stream.Stream;
6+
7+
import static java.util.Arrays.stream;
8+
9+
public class EnumTypeExtractor {
10+
11+
Stream<Class<? extends Enum<?>>> getEnumTypesRelevantFor(Type type) {
12+
if (type instanceof Class && ((Class<?>) type).isEnum()) {
13+
return Stream.of((Class<? extends Enum<?>>) type);
14+
}
15+
if (type instanceof ParameterizedType) {
16+
ParameterizedType parameterizedType = (ParameterizedType) type;
17+
return stream(parameterizedType.getActualTypeArguments())
18+
.flatMap(this::getEnumTypesRelevantFor)
19+
.distinct();
20+
}
21+
return Stream.empty();
22+
}
23+
}

src/main/java/com/tngtech/configbuilder/util/FieldValueTransformer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class FieldValueTransformer {
2020
private final ConfigBuilderFactory configBuilderFactory;
2121
private final ErrorMessageSetup errorMessageSetup;
2222
private final GenericsAndCastingHelper genericsAndCastingHelper;
23+
private final EnumTypeExtractor enumTypeExtractor;
2324
private Object[] additionalOptions;
2425

2526
//Order is important: Prefer List over Set if both apply!
@@ -38,6 +39,7 @@ public FieldValueTransformer(ConfigBuilderFactory configBuilderFactory) {
3839
this.configBuilderFactory = configBuilderFactory;
3940
this.errorMessageSetup = configBuilderFactory.getInstance(ErrorMessageSetup.class);
4041
this.genericsAndCastingHelper = configBuilderFactory.getInstance(GenericsAndCastingHelper.class);
42+
this.enumTypeExtractor = configBuilderFactory.getInstance(EnumTypeExtractor.class);
4143
}
4244

4345
public Object transformFieldValue(Field field, Object sourceValue) {
@@ -51,6 +53,10 @@ private void initialize(Field field) {
5153
for(Class<? extends TypeTransformer> transformerClass : getAllTransformers(field)) {
5254
availableTransformers.add(configBuilderFactory.getInstance(transformerClass));
5355
}
56+
enumTypeExtractor.getEnumTypesRelevantFor(field.getGenericType())
57+
.map(enumClass -> new StringToEnumTypeTransformer(enumClass))
58+
.forEach(availableTransformers::add);
59+
5460
additionalOptions = field.isAnnotationPresent(Separator.class)? new Object[]{field.getAnnotation(Separator.class).value()} : new Object[]{","};
5561
}
5662

src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToEnumTypeTransformerTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,5 @@ public void testTransform() {
5151
public void testIsMatching() {
5252
assertThat(transformer.isMatching(String.class, TestEnum.class)).isTrue();
5353
assertThat(transformer.isMatching(String.class, Integer.class)).isFalse();
54-
assertThat(transformer.isMatching(String.class, Enum.class)).isTrue();
5554
}
5655
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.tngtech.configbuilder.util;
2+
3+
import org.junit.Test;
4+
5+
import java.lang.reflect.Field;
6+
import java.util.List;
7+
import java.util.Map;
8+
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
public class EnumTypeExtractorTest {
12+
13+
enum TestEnum1 {}
14+
15+
enum TestEnum2 {}
16+
17+
public static class TestClass {
18+
19+
String simpleNonEnumField;
20+
Map<Integer, String> nonEnumParameterizedField;
21+
22+
TestEnum1 simpleEnumField;
23+
Map<TestEnum1, TestEnum2> fieldWithEnumTypeParameters;
24+
Map<TestEnum1, List<TestEnum2>> fieldWithNestedEnumTypeParameters;
25+
Map<TestEnum1, Map<TestEnum1, TestEnum1>> fieldWithRepeatedEnumTypeParameter;
26+
}
27+
28+
private final EnumTypeExtractor enumTypeExtractor = new EnumTypeExtractor();
29+
30+
@Test
31+
public void testExtractionOnSimpleNonEnumType() throws Exception {
32+
Field simpleNonEnumField = TestClass.class.getDeclaredField("simpleNonEnumField");
33+
assertThat(enumTypeExtractor.getEnumTypesRelevantFor(simpleNonEnumField.getGenericType()))
34+
.isEmpty();
35+
}
36+
37+
@Test
38+
public void testExtractionOnNonEnumParameterizedType() throws Exception {
39+
Field nonEnumParameterizedField = TestClass.class.getDeclaredField("nonEnumParameterizedField");
40+
assertThat(enumTypeExtractor.getEnumTypesRelevantFor(nonEnumParameterizedField.getGenericType()))
41+
.isEmpty();
42+
}
43+
44+
@Test
45+
public void testExtractionOfSimpleEnumType() throws Exception {
46+
Field simpleEnumField = TestClass.class.getDeclaredField("simpleEnumField");
47+
assertThat(enumTypeExtractor.getEnumTypesRelevantFor(simpleEnumField.getGenericType()))
48+
.containsExactly(TestEnum1.class);
49+
}
50+
51+
@Test
52+
public void testExtractionOfEnumTypeParameters() throws Exception {
53+
Field fieldWithEnumTypeParameters = TestClass.class.getDeclaredField("fieldWithEnumTypeParameters");
54+
assertThat(enumTypeExtractor.getEnumTypesRelevantFor(fieldWithEnumTypeParameters.getGenericType()))
55+
.containsExactlyInAnyOrder(TestEnum1.class, TestEnum2.class);
56+
}
57+
58+
@Test
59+
public void testExtractionOfNestedEnumTypeParameters() throws Exception {
60+
Field fieldWithNestedEnumTypeParameters = TestClass.class.getDeclaredField("fieldWithNestedEnumTypeParameters");
61+
assertThat(enumTypeExtractor.getEnumTypesRelevantFor(fieldWithNestedEnumTypeParameters.getGenericType()))
62+
.containsExactlyInAnyOrder(TestEnum1.class, TestEnum2.class);
63+
}
64+
65+
@Test
66+
public void testExtractedEnumTypesAreDeduplicated() throws Exception {
67+
Field fieldWithRepeatedEnumTypeParameter = TestClass.class.getDeclaredField("fieldWithRepeatedEnumTypeParameter");
68+
assertThat(enumTypeExtractor.getEnumTypesRelevantFor(fieldWithRepeatedEnumTypeParameter.getGenericType()))
69+
.containsExactly(TestEnum1.class);
70+
}
71+
}

src/test/java/com/tngtech/configbuilder/util/FieldValueTransformerComponentTest.java

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.tngtech.configbuilder.util;
22

33
import com.tngtech.configbuilder.annotation.typetransformer.*;
4-
import com.tngtech.configbuilder.configuration.BuilderConfiguration;
54
import com.tngtech.configbuilder.configuration.ErrorMessageSetup;
65
import com.tngtech.configbuilder.exception.PrimitiveParsingException;
76
import org.junit.Before;
@@ -14,30 +13,31 @@
1413
import java.nio.file.Path;
1514
import java.nio.file.Paths;
1615
import java.util.Collection;
16+
import java.util.EnumSet;
1717
import java.util.List;
18+
import java.util.Set;
1819

1920
import static com.google.common.collect.Lists.newArrayList;
21+
import static com.tngtech.configbuilder.util.FieldValueTransformerComponentTest.TestEnum.BAR;
22+
import static com.tngtech.configbuilder.util.FieldValueTransformerComponentTest.TestEnum.FOO;
2023
import static org.assertj.core.api.Assertions.assertThat;
2124
import static org.mockito.Mockito.when;
2225

2326
@RunWith(MockitoJUnitRunner.class)
2427
public class FieldValueTransformerComponentTest {
2528

26-
public class TestTransformer extends TypeTransformer<String, Integer> {
29+
public static class TestTransformer extends TypeTransformer<String, Integer> {
2730
@Override
2831
public Integer transform(String argument) {
2932
return 1472;
3033
}
3134
}
3235

33-
public static class AnotherTestTransformer extends TypeTransformer<String, Integer> {
34-
@Override
35-
public Integer transform(String argument) {
36-
return 1472;
37-
}
36+
enum TestEnum {
37+
FOO, BAR
3838
}
3939

40-
private class TestConfigClass {
40+
private static class TestConfigClass {
4141

4242
@TypeTransformers({CharacterSeparatedStringToStringListTransformer.class})
4343
private Collection<String> stringCollectionField;
@@ -46,36 +46,38 @@ private class TestConfigClass {
4646
private Boolean boolField;
4747
@TypeTransformers({TestTransformer.class})
4848
private int otherIntField;
49-
@TypeTransformers({AnotherTestTransformer.class})
50-
private Integer integerField;
5149
private Collection<Path> pathCollectionField;
5250
private Collection<Integer> integerCollectionField;
5351
private Collection<Object> objectCollectionField;
52+
private TestEnum enumField;
53+
private List<TestEnum> enumListField;
54+
private Set<TestEnum> enumSetField;
5455
}
5556

5657
@Mock
5758
private ConfigBuilderFactory configBuilderFactory;
5859
@Mock
5960
private ErrorMessageSetup errorMessageSetup;
60-
@Mock
61-
private BuilderConfiguration builderConfiguration;
61+
62+
private FieldValueTransformer fieldValueTransformer;
6263

6364
private Field stringCollectionField;
6465
private Field intField;
6566
private Field doubleField;
6667
private Field boolField;
6768
private Field otherIntField;
68-
private Field integerField;
6969
private Field pathCollectionField;
7070
private Field integerCollectionField;
7171
private Field objectCollectionField;
72-
73-
private FieldValueTransformer fieldValueTransformer;
72+
private Field enumField;
73+
private Field enumListField;
74+
private Field enumSetField;
7475

7576
@Before
7677
public void setUp() throws Exception {
7778
when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(errorMessageSetup);
7879
when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper());
80+
when(configBuilderFactory.getInstance(EnumTypeExtractor.class)).thenReturn(new EnumTypeExtractor());
7981

8082
when(configBuilderFactory.getInstance(CharacterSeparatedStringToStringListTransformer.class)).thenReturn(new CharacterSeparatedStringToStringListTransformer());
8183
when(configBuilderFactory.getInstance(CharacterSeparatedStringToStringSetTransformer.class)).thenReturn(new CharacterSeparatedStringToStringSetTransformer());
@@ -86,23 +88,25 @@ public void setUp() throws Exception {
8688
when(configBuilderFactory.getInstance(StringOrPrimitiveToPrimitiveTransformer.class)).thenReturn(new StringOrPrimitiveToPrimitiveTransformer());
8789
when(configBuilderFactory.getInstance(TestTransformer.class)).thenReturn(new TestTransformer());
8890

91+
this.fieldValueTransformer = new FieldValueTransformer(configBuilderFactory);
92+
8993
stringCollectionField = TestConfigClass.class.getDeclaredField("stringCollectionField");
9094
intField = TestConfigClass.class.getDeclaredField("intField");
9195
boolField = TestConfigClass.class.getDeclaredField("boolField");
9296
otherIntField = TestConfigClass.class.getDeclaredField("otherIntField");
93-
integerField = TestConfigClass.class.getDeclaredField("integerField");
9497
pathCollectionField = TestConfigClass.class.getDeclaredField("pathCollectionField");
9598
integerCollectionField = TestConfigClass.class.getDeclaredField("integerCollectionField");
9699
doubleField = TestConfigClass.class.getDeclaredField("doubleField");
97100
objectCollectionField = TestConfigClass.class.getDeclaredField("objectCollectionField");
98-
99-
this.fieldValueTransformer = new FieldValueTransformer(configBuilderFactory);
101+
enumField = TestConfigClass.class.getDeclaredField("enumField");
102+
enumListField = TestConfigClass.class.getDeclaredField("enumListField");
103+
enumSetField = TestConfigClass.class.getDeclaredField("enumSetField");
100104
}
101105

102106
@Test
103107
public void testTransformingStringToStringCollection() {
104-
List<String> actualResult = (List<String>) fieldValueTransformer.transformFieldValue(stringCollectionField, "Alpha,Beta,Gamma");
105-
assertThat(actualResult).containsExactly("Alpha", "Beta", "Gamma");
108+
Object actualResult = fieldValueTransformer.transformFieldValue(stringCollectionField, "Alpha,Beta,Gamma");
109+
assertThat(actualResult).isEqualTo(newArrayList("Alpha", "Beta", "Gamma"));
106110
}
107111

108112
@Test
@@ -136,25 +140,43 @@ public void testThatTransformersInAnnotationArePrioritized() {
136140

137141
@Test
138142
public void testTransformingStringToPathCollection() {
139-
Collection<Path> actualResult = (Collection<Path>) fieldValueTransformer.transformFieldValue(pathCollectionField, "/etc,/usr");
143+
Object actualResult = fieldValueTransformer.transformFieldValue(pathCollectionField, "/etc,/usr");
140144
assertThat(actualResult).isEqualTo(newArrayList(Paths.get("/etc"), Paths.get("/usr")));
141145
}
142146

143147
@Test
144148
public void testTransformingStringToIntegerCollection() {
145-
Collection<Integer> actualResult = (Collection<Integer>) fieldValueTransformer.transformFieldValue(integerCollectionField, "3,4");
149+
Object actualResult = fieldValueTransformer.transformFieldValue(integerCollectionField, "3,4");
146150
assertThat(actualResult).isEqualTo(newArrayList(3, 4));
147151
}
148152

149153
@Test
150154
public void testTransformingStringToObjectCollection() {
151-
Collection<Object> actualResult = (Collection<Object>) fieldValueTransformer.transformFieldValue(objectCollectionField, "someString,anotherString");
155+
Object actualResult = fieldValueTransformer.transformFieldValue(objectCollectionField, "someString,anotherString");
152156
assertThat(actualResult).isEqualTo(newArrayList("someString", "anotherString"));
153157
}
154158

159+
@Test
160+
public void testTransformingStringToEnum() {
161+
Object actualResult = fieldValueTransformer.transformFieldValue(enumField, "FOO");
162+
assertThat(actualResult).isEqualTo(FOO);
163+
}
164+
165+
@Test
166+
public void testTransformingStringToEnumList() {
167+
Object actualResult = fieldValueTransformer.transformFieldValue(enumListField, "FOO, BAR, FOO");
168+
assertThat(actualResult).isEqualTo(newArrayList(FOO, BAR, FOO));
169+
}
170+
171+
@Test
172+
public void testTransformingStringToEnumSet() {
173+
Object actualResult = fieldValueTransformer.transformFieldValue(enumSetField, "BAR, FOO");
174+
assertThat(actualResult).isEqualTo(EnumSet.of(BAR, FOO));
175+
}
176+
155177
@Test
156178
public void testThatValueTransformerIgnoresNull() {
157-
Collection<Path> actualResult = (Collection<Path>) fieldValueTransformer.transformFieldValue(pathCollectionField, null);
179+
Object actualResult = fieldValueTransformer.transformFieldValue(pathCollectionField, null);
158180
assertThat(actualResult).isNull();
159181
}
160-
}
182+
}

src/test/java/com/tngtech/configbuilder/util/FieldValueTransformerTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public class FieldValueTransformerTest {
3636
@Mock
3737
private GenericsAndCastingHelper genericsAndCastingHelper;
3838
@Mock
39+
private EnumTypeExtractor enumTypeExtractor;
40+
@Mock
3941
private CollectionToArrayListTransformer collectionToArrayListTransformer;
4042
@Mock
4143
private CharacterSeparatedStringToStringListTransformer characterSeparatedStringToStringListTransformer;
@@ -59,6 +61,7 @@ public class FieldValueTransformerTest {
5961
public void setUp() throws Exception {
6062
when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(errorMessageSetup);
6163
when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(genericsAndCastingHelper);
64+
when(configBuilderFactory.getInstance(EnumTypeExtractor.class)).thenReturn(enumTypeExtractor);
6265

6366
fieldValueTransformer = new FieldValueTransformer(configBuilderFactory);
6467
field = this.getClass().getDeclaredField("testField");

0 commit comments

Comments
 (0)