Fix: Converting back fails for nested collections of complex ConfigurationElements

Note that you now have to set a nesting level when using nested
collections that require the ElementType annotation.
dev v2.0.3
Exlll 6 years ago
parent ee53d0c609
commit b8caedc86d

@ -1,5 +1,5 @@
name: ConfigLib
author: Exlll
version: 2.0.2
version: 2.0.3
main: de.exlll.configlib.ConfigLib

@ -1,5 +1,5 @@
name: ConfigLib
author: Exlll
version: 2.0.2
version: 2.0.3
main: de.exlll.configlib.ConfigLib

@ -67,6 +67,8 @@ public interface Converter<F, T> {
private final Class<?> elementType;
private final String fieldName;
private final Configuration.Properties props;
private final int nestingLevel;
private int currentNestingLevel;
private ConversionInfo(Field field, Object instance, Object mapValue,
Configuration.Properties props) {
@ -79,6 +81,7 @@ public interface Converter<F, T> {
this.fieldName = field.getName();
this.props = props;
this.elementType = elementType(field);
this.nestingLevel = nestingLevel(field);
}
private static Class<?> elementType(Field field) {
@ -89,6 +92,14 @@ public interface Converter<F, T> {
return null;
}
private static int nestingLevel(Field field) {
if (field.isAnnotationPresent(ElementType.class)) {
ElementType et = field.getAnnotation(ElementType.class);
return et.nestingLevel();
}
return -1;
}
static ConversionInfo of(Field field, Object instance,
Configuration.Properties props) {
return new ConversionInfo(field, instance, null, props);
@ -192,5 +203,17 @@ public interface Converter<F, T> {
public boolean hasElementType() {
return elementType != null;
}
int getNestingLevel() {
return nestingLevel;
}
int getCurrentNestingLevel() {
return currentNestingLevel;
}
void incCurrentNestingLevel() {
currentNestingLevel++;
}
}
}

@ -344,26 +344,48 @@ final class Converters {
private static Function<Object, ?> createToConversionFunction(
Object element, ConversionInfo info
) {
return o -> selectNonSimpleConverter(element.getClass(), info)
.convertTo(o, info);
checkNestingLevel(element, info);
if (Reflect.isContainerType(element.getClass())) {
info.incCurrentNestingLevel();
}
Converter<Object, ?> converter = selectNonSimpleConverter(
element.getClass(), info
);
return o -> converter.convertTo(o, info);
}
private static Function<Object, ?> createFromConversionFunction(
Object element, ConversionInfo info
) {
if ((element instanceof Map<?, ?>) && isTypeMap((Map<?, ?>) element)) {
boolean currentLevelSameAsExpected =
info.getNestingLevel() == info.getCurrentNestingLevel();
checkCurrentLevelSameAsExpectedRequiresMapOrString(
currentLevelSameAsExpected, element, info
);
if ((element instanceof Map<?, ?>) && currentLevelSameAsExpected) {
return o -> {
Map<String, Object> map = toTypeMap(o, null);
Object inst = Reflect.newInstance(info.getElementType());
FieldMapper.instanceFromMap(inst, map, info.getProperties());
return inst;
};
} else if ((element instanceof String) && currentLevelSameAsExpected) {
return createNonSimpleConverter(element, info);
} else {
return o -> selectNonSimpleConverter(element.getClass(), info)
.convertFrom(o, info);
info.incCurrentNestingLevel();
return createNonSimpleConverter(element, info);
}
}
private static Function<Object, ?> createNonSimpleConverter(
Object element, ConversionInfo info
) {
Converter<?, Object> converter = selectNonSimpleConverter(
element.getClass(), info
);
return o -> converter.convertFrom(o, info);
}
private static Map<String, Object> toTypeMap(Object value, String fn) {
checkIsMap(value, fn);
checkMapKeysAreStrings((Map<?, ?>) value, fn);

@ -315,6 +315,37 @@ final class Validator {
}
}
static void checkNestingLevel(Object element, ConversionInfo info) {
if (!Reflect.isContainerType(element.getClass())) {
if (info.getNestingLevel() != info.getCurrentNestingLevel()) {
String msg = "Field '" + info.getFieldName() + "' of class " +
"'" + getClsName(info.getInstance().getClass()) + "' " +
"has a nesting level of " + info.getNestingLevel() +
" but the first object of type '" +
getClsName(info.getElementType()) + "' was found on " +
"level " + info.getCurrentNestingLevel() + ".";
throw new ConfigurationException(msg);
}
}
}
static void checkCurrentLevelSameAsExpectedRequiresMapOrString(
boolean currentLevelSameAsExpected,
Object element, ConversionInfo info
) {
boolean isMapOrString = (element instanceof Map<?, ?>) ||
(element instanceof String);
if (currentLevelSameAsExpected && !isMapOrString) {
Class<?> cls = info.getInstance().getClass();
String msg = "Field '" + info.getFieldName() + "' of class '" +
getClsName(cls) + "' has a nesting level" +
" of " + info.getNestingLevel() + " but element '" + element +
"' of type '" + getClsName(element.getClass()) + "' cannot be " +
"converted to '" + getClsName(info.getElementType()) + "'.";
throw new ConfigurationException(msg);
}
}
static void checkElementTypeIsEnumType(Class<?> type, ConversionInfo info) {
if (!Reflect.isEnumType(type)) {
String msg = "Element type '" + getClsName(type) + "' of field " +

@ -10,9 +10,21 @@ import java.lang.annotation.Target;
* This annotation must only be used if a {@code Collection} or {@code Map} contains
* elements whose type is not simple. Note that {@code Map} keys can only be of some
* simple type.
* <p>
* If collections are nested, the {@code nestingLevel} must be set. Examples:
* <ul>
* <li>nestingLevel 1: {@code List<List<T>>}</li>
* <li>nestingLevel 1: {@code List<Set<T>>}</li>
* <li>nestingLevel 1: {@code List<Map<String, T>>}</li>
* <li>nestingLevel 2: {@code List<List<List<T>>>}</li>
* <li>nestingLevel 2: {@code List<Set<List<T>>>}</li>
* <li>nestingLevel 2: {@code List<List<Map<String, T>>>}</li>
* </ul>
*/
@Target(java.lang.annotation.ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ElementType {
Class<?> value();
int nestingLevel() default 0;
}

@ -342,7 +342,7 @@ public class FieldMapperConverterTest {
@Test
void instanceFromMapCatchesClassCastExceptionOfUnknownEnumConstantsInLists() {
class A {
@ElementType(LocalTestEnum.class)
@ElementType(value = LocalTestEnum.class, nestingLevel = 1)
List<List<LocalTestEnum>> l = listOf();
}
Map<String, Object> map = mapOf(
@ -359,7 +359,7 @@ public class FieldMapperConverterTest {
@Test
void instanceFromMapCatchesClassCastExceptionOfUnknownEnumConstantsInSets() {
class A {
@ElementType(LocalTestEnum.class)
@ElementType(value = LocalTestEnum.class, nestingLevel = 1)
Set<List<LocalTestEnum>> s = setOf();
}
Map<String, Object> map = mapOf(
@ -376,7 +376,7 @@ public class FieldMapperConverterTest {
@Test
void instanceFromMapCatchesClassCastExceptionOfUnknownEnumConstantsInMaps() {
class A {
@ElementType(LocalTestEnum.class)
@ElementType(value = LocalTestEnum.class, nestingLevel = 1)
Map<Integer, List<LocalTestEnum>> m = mapOf();
}
Map<String, Object> map = mapOf(

@ -12,9 +12,7 @@ import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CopyOnWriteArrayList;
import static de.exlll.configlib.FieldMapperHelpers.*;
import static de.exlll.configlib.util.CollectionFactory.listOf;
import static de.exlll.configlib.util.CollectionFactory.mapOf;
import static de.exlll.configlib.util.CollectionFactory.setOf;
import static de.exlll.configlib.util.CollectionFactory.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
@ -117,12 +115,12 @@ public class ValidatorTest {
m = mapOf("s", setOf("s"));
msg = "Can not set field 's' with type 'ConcurrentSkipListSet' " +
"to 'HashSet'.";
"to 'LinkedHashSet'.";
assertIfmCfgExceptionMessage(new B(), m, msg);
m = mapOf("m", mapOf(1, "s"));
msg = "Can not set field 'm' with type 'ConcurrentHashMap' " +
"to 'HashMap'.";
"to 'LinkedHashMap'.";
assertIfmCfgExceptionMessage(new C(), m, msg);
}
@ -152,25 +150,27 @@ public class ValidatorTest {
void instanceToMapRequiresListsWithoutElementTypeToContainSimpleTypes() {
class A {
List<TestSubClass> l = new ArrayList<>(listOf(
TestSubClass.of(1, "1")
TestSubClass.TEST_VALUES
));
}
class B {
List<Set<Map<Integer, TestSubClass>>> l = new ArrayList<>(listOf(
setOf(mapOf(1, TestSubClass.of(1, "1")))
setOf(mapOf(1, TestSubClass.TEST_VALUES))
));
}
String asString = TestSubClass.TEST_VALUES.toString();
A a = new A();
String msg = "The type of an element of list 'l' is not a simple type " +
"but list 'l' is missing the ElementType annotation.\n" +
"All elements: [TestSubClass{\nprimInt=1,\nstring='1'}]";
"All elements: [" + asString + "]";
assertItmCfgExceptionMessage(a, msg);
B b = new B();
msg = "The type of an element of list 'l' is not a simple type " +
"but list 'l' is missing the ElementType annotation.\n" +
"All elements: [[{1=TestSubClass{\nprimInt=1,\nstring='1'}}]]";
"All elements: [[{1=" + asString + "}]]";
assertItmCfgExceptionMessage(b, msg);
}
@ -178,25 +178,27 @@ public class ValidatorTest {
void instanceToMapRequiresSetsWithoutElementTypeToContainSimpleTypes() {
class A {
Set<TestSubClass> s = new HashSet<>(setOf(
TestSubClass.of(1, "1")
TestSubClass.TEST_VALUES
));
}
class B {
Set<List<Map<Integer, TestSubClass>>> s = new HashSet<>(setOf(
listOf(mapOf(1, TestSubClass.of(1, "1")))
listOf(mapOf(1, TestSubClass.TEST_VALUES))
));
}
String asString = TestSubClass.TEST_VALUES.toString();
A a = new A();
String msg = "The type of an element of set 's' is not a simple type " +
"but set 's' is missing the ElementType annotation.\n" +
"All elements: [TestSubClass{\nprimInt=1,\nstring='1'}]";
"All elements: [" + asString + "]";
assertItmCfgExceptionMessage(a, msg);
B b = new B();
msg = "The type of an element of set 's' is not a simple type " +
"but set 's' is missing the ElementType annotation.\n" +
"All elements: [[{1=TestSubClass{\nprimInt=1,\nstring='1'}}]]";
"All elements: [[{1=" + asString + "}]]";
assertItmCfgExceptionMessage(b, msg);
}
@ -204,25 +206,27 @@ public class ValidatorTest {
void instanceToMapRequiresMapsWithoutElementTypeToContainSimpleTypes() {
class A {
Map<Integer, TestSubClass> m = new HashMap<>(mapOf(
1, TestSubClass.of(1, "1")
1, TestSubClass.TEST_VALUES
));
}
class B {
Map<Integer, Set<List<TestSubClass>>> m = new HashMap<>(mapOf(
1, setOf(listOf(TestSubClass.of(1, "1")))
1, setOf(listOf(TestSubClass.TEST_VALUES))
));
}
String asString = TestSubClass.TEST_VALUES.toString();
A a = new A();
String msg = "The type of a value of map 'm' is not a simple type " +
"but map 'm' is missing the ElementType annotation.\n" +
"All entries: {1=TestSubClass{\nprimInt=1,\nstring='1'}}";
"All entries: {1=" + asString + "}";
assertItmCfgExceptionMessage(a, msg);
B b = new B();
msg = "The type of a value of map 'm' is not a simple type " +
"but map 'm' is missing the ElementType annotation.\n" +
"All entries: {1=[[TestSubClass{\nprimInt=1,\nstring='1'}]]}";
"All entries: {1=[[" + asString + "]]}";
assertItmCfgExceptionMessage(b, msg);
}
@ -503,7 +507,7 @@ public class ValidatorTest {
@Test
void instanceFromMapsRequiresElementTypeToBeEnumType() {
class A {
@ElementType(TestSubClass.class)
@ElementType(value = TestSubClass.class, nestingLevel = 1)
List<List<TestSubClass>> l = listOf();
}
Map<String, Object> map = mapOf(
@ -519,17 +523,146 @@ public class ValidatorTest {
@Test
void instanceFromMapElementConverterRequiresObjectsOfTypeMapStringObject() {
class A {
@ElementType(TestSubClass.class)
@ElementType(value = TestSubClass.class, nestingLevel = 1)
List<List<TestSubClass>> l = listOf();
}
Map<String, Object> map = mapOf(
"l", listOf(listOf(1, 2))
);
ConfigurationException ex = assertIfmThrowsCfgException(new A(), map);
Throwable cause = ex.getCause();
String msg = "Field 'l' of class 'A' has a nesting level of 1 but element " +
"'1' of type 'Integer' cannot be converted to 'TestSubClass'.";
assertIfmCfgExceptionMessage(new A(), map, msg);
}
String msg = "Initializing field 'l' requires objects of type " +
"Map<String, Object> but element '1' is of type 'Integer'.";
assertThat(cause.getMessage(), is(msg));
@Test
void instanceToMapRequiresCorrectNestingLevelForLists() {
TestSubClass testValues = TestSubClass.TEST_VALUES;
class A {
@ElementType(TestSubClass.class)
List<List<TestSubClass>> l1 = listOf();
@ElementType(TestSubClass.class)
List<List<TestSubClass>> l2 = listOf(listOf(testValues));
}
class B {
@ElementType(value = TestSubClass.class, nestingLevel = 1)
List<List<List<TestSubClass>>> l = listOf(listOf(listOf(testValues)));
}
class C {
@ElementType(value = TestSubClass.class, nestingLevel = 3)
List<List<List<TestSubClass>>> l = listOf(listOf(listOf(testValues)));
}
class D {
@ElementType(value = TestSubClass.class, nestingLevel = 1)
List<List<TestSubClass>> l = listOf(listOf(
TestSubClass.of(11, "11"), TestSubClass.of(12, "12"),
TestSubClass.of(13, "13"), TestSubClass.of(14, "14")
));
}
String msg = "Field 'l2' of class 'A' has a nesting level of 0 but the " +
"first object of type 'TestSubClass' was found on level 1.";
assertItmCfgExceptionMessage(new A(), msg);
msg = "Field 'l' of class 'B' has a nesting level of 1 but the " +
"first object of type 'TestSubClass' was found on level 2.";
assertItmCfgExceptionMessage(new B(), msg);
msg = "Field 'l' of class 'C' has a nesting level of 3 but the " +
"first object of type 'TestSubClass' was found on level 2.";
assertItmCfgExceptionMessage(new C(), msg);
Map<String, Object> map = instanceToMap(new D());
D d = instanceFromMap(new D(), map);
assertThat(d.l, is(new D().l));
}
@Test
void instanceToMapRequiresCorrectNestingLevelForMaps() {
TestSubClass testValues = TestSubClass.TEST_VALUES;
class A {
@ElementType(TestSubClass.class)
Map<String, Map<String, TestSubClass>> m1 = mapOf();
@ElementType(TestSubClass.class)
Map<Integer, Map<String, TestSubClass>> m2 = mapOf(
1, mapOf("1", TestSubClass.of(11, "11")),
2, mapOf("2", TestSubClass.of(12, "12"))
);
}
class B {
@ElementType(value = TestSubClass.class, nestingLevel = 1)
Map<String, Map<String, Map<String, TestSubClass>>> m = mapOf(
"1", mapOf("2", mapOf("3", testValues)),
"1", mapOf("2", mapOf("3", testValues))
);
}
class C {
@ElementType(value = TestSubClass.class, nestingLevel = 3)
Map<String, Map<String, Map<String, TestSubClass>>> m = mapOf(
"1", mapOf("2", mapOf("3", testValues)),
"1", mapOf("2", mapOf("3", testValues))
);
}
class D {
@ElementType(value = TestSubClass.class, nestingLevel = 1)
Map<Integer, Map<String, TestSubClass>> m = mapOf(
1, mapOf("1", TestSubClass.of(11, "11")),
2, mapOf("2", TestSubClass.of(12, "12")),
3, mapOf("3", TestSubClass.of(13, "13")),
4, mapOf("4", TestSubClass.of(14, "14"))
);
}
String msg = "Field 'm2' of class 'A' has a nesting level of 0 but the " +
"first object of type 'TestSubClass' was found on level 1.";
assertItmCfgExceptionMessage(new A(), msg);
msg = "Field 'm' of class 'B' has a nesting level of 1 but the " +
"first object of type 'TestSubClass' was found on level 2.";
assertItmCfgExceptionMessage(new B(), msg);
msg = "Field 'm' of class 'C' has a nesting level of 3 but the " +
"first object of type 'TestSubClass' was found on level 2.";
assertItmCfgExceptionMessage(new C(), msg);
Map<String, Object> map = instanceToMap(new D());
D d = instanceFromMap(new D(), map);
assertThat(d.m, is(new D().m));
}
/* The case that the nestingLevel is set to high cannot properly be detected. */
@Test
void instanceFromMapRequiresCorrectNestingLevelForLists() {
class A {
@ElementType(TestSubClass.class)
List<TestSubClass> l = listOf();
}
class B {
@ElementType(LocalTestEnum.class)
List<LocalTestEnum> l = listOf();
}
class C {
@ElementType(TestSubClass.class)
List<List<TestSubClass>> l = listOf();
}
class D {
@ElementType(LocalTestEnum.class)
List<List<LocalTestEnum>> l = listOf();
}
Map<String, Object> m = TestSubClass.TEST_VALUES.asMap();
instanceFromMap(new A(), mapOf("l", listOf(m)));
instanceFromMap(new B(), mapOf("l", listOf("S", "T")));
String elementAsString = m.toString();
String msg = "Field 'l' of class 'C' has a nesting level of 0 but element '[" +
elementAsString + "]' of type 'ArrayList' cannot be converted " +
"to 'TestSubClass'.";
assertIfmCfgExceptionMessage(new C(), mapOf("l", listOf(listOf(m))), msg);
msg = "Field 'l' of class 'D' has a nesting level of 0 but element '[S, T]' of type " +
"'ArrayList' cannot be converted to 'LocalTestEnum'.";
assertIfmCfgExceptionMessage(new D(), mapOf("l", listOf(listOf("S", "T"))), msg);
}
}

@ -10,9 +10,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import static de.exlll.configlib.util.CollectionFactory.listOf;
import static de.exlll.configlib.util.CollectionFactory.mapOf;
import static java.util.stream.Collectors.toCollection;
import static de.exlll.configlib.util.CollectionFactory.*;
@SuppressWarnings("FieldCanBeLocal")
@Comment({"A", "", "B", "C"})
@ -45,59 +43,49 @@ public final class TestClass extends YamlConfiguration {
/* other types */
TEST_VALUES.subClass = TestSubClass.TEST_VALUES;
/* containers of simple types */
TEST_VALUES.ints = linkedHashSetOf(1, 2, 3);
TEST_VALUES.ints = setOf(1, 2, 3);
TEST_VALUES.strings = listOf("a", "b", "c");
TEST_VALUES.doubleByBool = linkedHashMap(true, 1.0, false, 2.0);
TEST_VALUES.doubleByBool = mapOf(true, 1.0, false, 2.0);
/* containers of other types */
TEST_VALUES.subClassSet = linkedHashSetOf(
TestSubClass.of(1, "1"), TestSubClass.of(2, "2")
TEST_VALUES.subClassSet = setOf(
TestSubClass.of(1, "1"),
TestSubClass.of(2, "2")
);
TEST_VALUES.subClassList = listOf(
TestSubClass.of(1, "1"), TestSubClass.of(2, "2")
TestSubClass.of(3, "3"),
TestSubClass.of(4, "4")
);
TEST_VALUES.subClassMap = linkedHashMap(
"1", TestSubClass.of(1, "1"),
"2", TestSubClass.of(2, "2")
TEST_VALUES.subClassMap = mapOf(
"5", TestSubClass.of(5, "5"),
"6", TestSubClass.of(6, "6")
);
/* nested containers of simple types */
TEST_VALUES.listsList = listOf(
listOf(1, 2), listOf(3, 4)
);
TEST_VALUES.setsSet = linkedHashSetOf(
linkedHashSetOf("a", "b"), linkedHashSetOf("c", "d")
TEST_VALUES.setsSet = setOf(
setOf("a", "b"), setOf("c", "d")
);
TEST_VALUES.mapsMap = linkedHashMap(
TEST_VALUES.mapsMap = mapOf(
1, mapOf("1", 1), 2, mapOf("2", 2)
);
/* nested containers of custom types */
TEST_VALUES.subClassListsList = listOf(
listOf(TestSubClass.of(1, "1"), TestSubClass.of(2, "2"))
listOf(TestSubClass.of(7, "7"), TestSubClass.of(8, "8"))
);
TEST_VALUES.subClassSetsSet = linkedHashSetOf(linkedHashSetOf(
TestSubClass.of(1, "1"), TestSubClass.of(2, "2")
TEST_VALUES.subClassSetsSet = setOf(setOf(
TestSubClass.of(9, "9"), TestSubClass.of(10, "10")
));
TEST_VALUES.subClassMapsMap = linkedHashMap(
1, mapOf("1", TestSubClass.of(1, "2")),
2, mapOf("2", TestSubClass.of(2, "2"))
TEST_VALUES.subClassMapsMap = mapOf(
1, mapOf("1", TestSubClass.of(11, "11")),
2, mapOf("2", TestSubClass.of(12, "12"))
);
TEST_VALUES.e1 = TestEnum.NON_DEFAULT;
TEST_VALUES.enums = listOf(TestEnum.DEFAULT, TestEnum.NON_DEFAULT);
TEST_VALUES.converterSubClass = TestSubClass.of(2, "2");
TEST_VALUES.converterSubClass = TestSubClass.of(13, "13");
TEST_VALUES.excludedClass = TestExcludedClass.TEST_VALUES;
}
@SafeVarargs
private static <T> Set<T> linkedHashSetOf(T... values) {
return Arrays.stream(values).collect(toCollection(LinkedHashSet::new));
}
private static <K, V> Map<K, V> linkedHashMap(K k1, V v1, K k2, V v2) {
Map<K, V> map = new LinkedHashMap<>();
map.put(k1, v1);
map.put(k2, v2);
return map;
}
/* not converted */
private static final int staticFinalInt = 1;
private static int staticInt = 2;
@ -144,11 +132,11 @@ public final class TestClass extends YamlConfiguration {
private Set<Set<String>> setsSet = new HashSet<>();
private Map<Integer, Map<String, Integer>> mapsMap = new HashMap<>();
/* nested containers of custom types */
@ElementType(TestSubClass.class)
@ElementType(value = TestSubClass.class, nestingLevel = 1)
private List<List<TestSubClass>> subClassListsList = new ArrayList<>();
@ElementType(TestSubClass.class)
@ElementType(value = TestSubClass.class, nestingLevel = 1)
private Set<Set<TestSubClass>> subClassSetsSet = new HashSet<>();
@ElementType(TestSubClass.class)
@ElementType(value = TestSubClass.class, nestingLevel = 1)
private Map<Integer, Map<String, TestSubClass>> subClassMapsMap
= new HashMap<>();
private TestEnum e1 = TestEnum.DEFAULT;

@ -1,10 +1,17 @@
package de.exlll.configlib.classes;
import de.exlll.configlib.annotation.ConfigurationElement;
import de.exlll.configlib.annotation.ElementType;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static de.exlll.configlib.util.CollectionFactory.mapOf;
import static de.exlll.configlib.util.CollectionFactory.*;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import static java.util.stream.Collectors.toSet;
@SuppressWarnings("FieldCanBeLocal")
@ConfigurationElement
@ -15,25 +22,73 @@ public final class TestSubClass {
TEST_VALUES = new TestSubClass();
TEST_VALUES.primInt = 1;
TEST_VALUES.string = "string";
TEST_VALUES.list = listOf("list");
TEST_VALUES.set = setOf("set");
TEST_VALUES.map = mapOf("map", 1);
TEST_VALUES.testSubSubClass = TestSubSubClass.of(14, "14");
TEST_VALUES.subClassList = listOf(
TestSubSubClass.of(15, "15"), TestSubSubClass.of(16, "16")
);
TEST_VALUES.subClassSet = setOf(
TestSubSubClass.of(17, "17"), TestSubSubClass.of(18, "18")
);
TEST_VALUES.subClassMap = mapOf("map", TestSubSubClass.of(19, "19"));
}
private final int finalInt = 1;
private int primInt;
private String string = "";
private List<String> list = Collections.emptyList();
private Set<String> set = Collections.emptySet();
private Map<String, Integer> map = Collections.emptyMap();
private TestSubSubClass testSubSubClass = new TestSubSubClass();
@ElementType(TestSubSubClass.class)
private List<TestSubSubClass> subClassList = Collections.emptyList();
@ElementType(TestSubSubClass.class)
private Set<TestSubSubClass> subClassSet = Collections.emptySet();
@ElementType(TestSubSubClass.class)
private Map<String, TestSubSubClass> subClassMap = Collections.emptyMap();
public static TestSubClass of(int primInt, String string) {
TestSubClass subClass = new TestSubClass();
subClass.primInt = primInt;
subClass.string = string;
return subClass;
TestSubClass cls = new TestSubClass();
cls.primInt = primInt;
cls.string = string;
cls.list = listOf(string);
cls.set = setOf(string);
cls.map = mapOf(string, primInt);
cls.testSubSubClass = TestSubSubClass.of(primInt, string);
cls.subClassList = listOf(
TestSubSubClass.of(primInt * 100, string)
);
cls.subClassSet = setOf(
TestSubSubClass.of(primInt * 101, string)
);
cls.subClassMap = mapOf(string, TestSubSubClass.of(primInt * 102, string));
return cls;
}
public Map<String, Object> asMap() {
return mapOf("primInt", primInt, "string", string);
}
public int getFinalInt() {
return finalInt;
Map<String, Object> asMap = mapOf("primInt", primInt, "string", string);
asMap.put("list", list);
asMap.put("set", set);
asMap.put("map", map);
asMap.put("testSubSubClass", testSubSubClass.asMap());
asMap.put(
"subClassList", subClassList.stream()
.map(TestSubSubClass::asMap)
.collect(toList())
);
asMap.put(
"subClassSet", subClassSet.stream()
.map(TestSubSubClass::asMap)
.collect(toSet())
);
asMap.put(
"subClassMap", subClassMap.entrySet().stream()
.map(e -> mapEntry(e.getKey(), e.getValue().asMap()))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue))
);
return asMap;
}
public int getPrimInt() {
@ -44,29 +99,79 @@ public final class TestSubClass {
return string;
}
public List<String> getList() {
return list;
}
public Set<String> getSet() {
return set;
}
public Map<String, Integer> getMap() {
return map;
}
public TestSubSubClass getTestSubSubClass() {
return testSubSubClass;
}
public List<TestSubSubClass> getSubClassList() {
return subClassList;
}
public Set<TestSubSubClass> getSubClassSet() {
return subClassSet;
}
public Map<String, TestSubSubClass> getSubClassMap() {
return subClassMap;
}
@Override
public String toString() {
return "TestSubClass{" +
"\nprimInt=" + primInt +
",\nstring='" + string + '\'' +
"primInt=" + primInt +
", string='" + string + '\'' +
", list=" + list +
", set=" + set +
", map=" + map +
", testSubSubClass=" + testSubSubClass +
", subClassList=" + subClassList +
", subClassSet=" + subClassSet +
", subClassMap=" + subClassMap +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TestSubClass)) return false;
TestSubClass subClass = (TestSubClass) o;
if (primInt != subClass.primInt) return false;
return string.equals(subClass.string);
if (o == null || getClass() != o.getClass()) return false;
TestSubClass that = (TestSubClass) o;
if (primInt != that.primInt) return false;
if (!string.equals(that.string)) return false;
if (!list.equals(that.list)) return false;
if (!set.equals(that.set)) return false;
if (!map.equals(that.map)) return false;
if (!testSubSubClass.equals(that.testSubSubClass)) return false;
if (!subClassList.equals(that.subClassList)) return false;
if (!subClassSet.equals(that.subClassSet)) return false;
return subClassMap.equals(that.subClassMap);
}
@Override
public int hashCode() {
int result = primInt;
int result = finalInt;
result = 31 * result + primInt;
result = 31 * result + string.hashCode();
result = 31 * result + list.hashCode();
result = 31 * result + set.hashCode();
result = 31 * result + map.hashCode();
result = 31 * result + testSubSubClass.hashCode();
result = 31 * result + subClassList.hashCode();
result = 31 * result + subClassSet.hashCode();
result = 31 * result + subClassMap.hashCode();
return result;
}
}

@ -0,0 +1,103 @@
package de.exlll.configlib.classes;
import de.exlll.configlib.annotation.ConfigurationElement;
import java.util.*;
import static de.exlll.configlib.util.CollectionFactory.listOf;
import static de.exlll.configlib.util.CollectionFactory.mapOf;
import static de.exlll.configlib.util.CollectionFactory.setOf;
@ConfigurationElement
public final class TestSubSubClass {
public static final TestSubSubClass TEST_VALUES;
static {
TEST_VALUES = new TestSubSubClass();
TEST_VALUES.primInt = 1;
TEST_VALUES.string = "string";
TEST_VALUES.list = listOf("list");
TEST_VALUES.set = setOf("set");
TEST_VALUES.map = mapOf("map", 1);
}
private int primInt;
private String string = "";
private List<String> list = listOf();
private Set<String> set = setOf();
private Map<String, Integer> map = mapOf();
public static TestSubSubClass of(int primInt, String string) {
TestSubSubClass cls = new TestSubSubClass();
cls.primInt = primInt;
cls.string = string;
String concat = string + string;
cls.list = listOf(concat);
cls.set = setOf(concat);
cls.map = mapOf(concat, primInt);
return cls;
}
public Map<String, Object> asMap() {
Map<String, Object> asMap = mapOf("primInt", primInt, "string", string);
asMap.put("list", list);
asMap.put("set", set);
asMap.put("map", map);
return asMap;
}
public int getPrimInt() {
return primInt;
}
public String getString() {
return string;
}
public List<String> getList() {
return list;
}
public Set<String> getSet() {
return set;
}
public Map<String, Integer> getMap() {
return map;
}
@Override
public String toString() {
return "TestSubSubClass{" +
"primInt=" + primInt +
", string='" + string + '\'' +
", list=" + list +
", set=" + set +
", map=" + map +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestSubSubClass that = (TestSubSubClass) o;
if (primInt != that.primInt) return false;
if (!string.equals(that.string)) return false;
if (!list.equals(that.list)) return false;
if (!set.equals(that.set)) return false;
return map.equals(that.map);
}
@Override
public int hashCode() {
int result = primInt;
result = 31 * result + string.hashCode();
result = 31 * result + list.hashCode();
result = 31 * result + set.hashCode();
result = 31 * result + map.hashCode();
return result;
}
}

@ -269,6 +269,67 @@ class YamlConfigurationTest {
"subClass:\n" +
" primInt: 1\n" +
" string: string\n" +
" list:\n" +
" - list\n" +
" set: !!set\n" +
" set: null\n" +
" map:\n" +
" map: 1\n" +
" testSubSubClass:\n" +
" primInt: 14\n" +
" string: '14'\n" +
" list:\n" +
" - '1414'\n" +
" set: !!set\n" +
" '1414': null\n" +
" map:\n" +
" '1414': 14\n" +
" subClassList:\n" +
" - primInt: 15\n" +
" string: '15'\n" +
" list:\n" +
" - '1515'\n" +
" set: !!set\n" +
" '1515': null\n" +
" map:\n" +
" '1515': 15\n" +
" - primInt: 16\n" +
" string: '16'\n" +
" list:\n" +
" - '1616'\n" +
" set: !!set\n" +
" '1616': null\n" +
" map:\n" +
" '1616': 16\n" +
" subClassSet: !!set\n" +
" ? primInt: 18\n" +
" string: '18'\n" +
" list:\n" +
" - '1818'\n" +
" set: !!set\n" +
" '1818': null\n" +
" map:\n" +
" '1818': 18\n" +
" : null\n" +
" ? primInt: 17\n" +
" string: '17'\n" +
" list:\n" +
" - '1717'\n" +
" set: !!set\n" +
" '1717': null\n" +
" map:\n" +
" '1717': 17\n" +
" : null\n" +
" subClassMap:\n" +
" map:\n" +
" primInt: 19\n" +
" string: '19'\n" +
" list:\n" +
" - '1919'\n" +
" set: !!set\n" +
" '1919': null\n" +
" map:\n" +
" '1919': 19\n" +
"ints: !!set\n" +
" 1: null\n" +
" 2: null\n" +
@ -283,22 +344,286 @@ class YamlConfigurationTest {
"subClassSet: !!set\n" +
" ? primInt: 1\n" +
" string: '1'\n" +
" list:\n" +
" - '1'\n" +
" set: !!set\n" +
" '1': null\n" +
" map:\n" +
" '1': 1\n" +
" testSubSubClass:\n" +
" primInt: 1\n" +
" string: '1'\n" +
" list:\n" +
" - '11'\n" +
" set: !!set\n" +
" '11': null\n" +
" map:\n" +
" '11': 1\n" +
" subClassList:\n" +
" - primInt: 100\n" +
" string: '1'\n" +
" list:\n" +
" - '11'\n" +
" set: !!set\n" +
" '11': null\n" +
" map:\n" +
" '11': 100\n" +
" subClassSet: !!set\n" +
" ? primInt: 101\n" +
" string: '1'\n" +
" list:\n" +
" - '11'\n" +
" set: !!set\n" +
" '11': null\n" +
" map:\n" +
" '11': 101\n" +
" : null\n" +
" subClassMap:\n" +
" '1':\n" +
" primInt: 102\n" +
" string: '1'\n" +
" list:\n" +
" - '11'\n" +
" set: !!set\n" +
" '11': null\n" +
" map:\n" +
" '11': 102\n" +
" : null\n" +
" ? primInt: 2\n" +
" string: '2'\n" +
" list:\n" +
" - '2'\n" +
" set: !!set\n" +
" '2': null\n" +
" map:\n" +
" '2': 2\n" +
" testSubSubClass:\n" +
" primInt: 2\n" +
" string: '2'\n" +
" list:\n" +
" - '22'\n" +
" set: !!set\n" +
" '22': null\n" +
" map:\n" +
" '22': 2\n" +
" subClassList:\n" +
" - primInt: 200\n" +
" string: '2'\n" +
" list:\n" +
" - '22'\n" +
" set: !!set\n" +
" '22': null\n" +
" map:\n" +
" '22': 200\n" +
" subClassSet: !!set\n" +
" ? primInt: 202\n" +
" string: '2'\n" +
" list:\n" +
" - '22'\n" +
" set: !!set\n" +
" '22': null\n" +
" map:\n" +
" '22': 202\n" +
" : null\n" +
" subClassMap:\n" +
" '2':\n" +
" primInt: 204\n" +
" string: '2'\n" +
" list:\n" +
" - '22'\n" +
" set: !!set\n" +
" '22': null\n" +
" map:\n" +
" '22': 204\n" +
" : null\n" +
"subClassList:\n" +
"- primInt: 1\n" +
" string: '1'\n" +
"- primInt: 2\n" +
" string: '2'\n" +
"- primInt: 3\n" +
" string: '3'\n" +
" list:\n" +
" - '3'\n" +
" set: !!set\n" +
" '3': null\n" +
" map:\n" +
" '3': 3\n" +
" testSubSubClass:\n" +
" primInt: 3\n" +
" string: '3'\n" +
" list:\n" +
" - '33'\n" +
" set: !!set\n" +
" '33': null\n" +
" map:\n" +
" '33': 3\n" +
" subClassList:\n" +
" - primInt: 300\n" +
" string: '3'\n" +
" list:\n" +
" - '33'\n" +
" set: !!set\n" +
" '33': null\n" +
" map:\n" +
" '33': 300\n" +
" subClassSet: !!set\n" +
" ? primInt: 303\n" +
" string: '3'\n" +
" list:\n" +
" - '33'\n" +
" set: !!set\n" +
" '33': null\n" +
" map:\n" +
" '33': 303\n" +
" : null\n" +
" subClassMap:\n" +
" '3':\n" +
" primInt: 306\n" +
" string: '3'\n" +
" list:\n" +
" - '33'\n" +
" set: !!set\n" +
" '33': null\n" +
" map:\n" +
" '33': 306\n" +
"- primInt: 4\n" +
" string: '4'\n" +
" list:\n" +
" - '4'\n" +
" set: !!set\n" +
" '4': null\n" +
" map:\n" +
" '4': 4\n" +
" testSubSubClass:\n" +
" primInt: 4\n" +
" string: '4'\n" +
" list:\n" +
" - '44'\n" +
" set: !!set\n" +
" '44': null\n" +
" map:\n" +
" '44': 4\n" +
" subClassList:\n" +
" - primInt: 400\n" +
" string: '4'\n" +
" list:\n" +
" - '44'\n" +
" set: !!set\n" +
" '44': null\n" +
" map:\n" +
" '44': 400\n" +
" subClassSet: !!set\n" +
" ? primInt: 404\n" +
" string: '4'\n" +
" list:\n" +
" - '44'\n" +
" set: !!set\n" +
" '44': null\n" +
" map:\n" +
" '44': 404\n" +
" : null\n" +
" subClassMap:\n" +
" '4':\n" +
" primInt: 408\n" +
" string: '4'\n" +
" list:\n" +
" - '44'\n" +
" set: !!set\n" +
" '44': null\n" +
" map:\n" +
" '44': 408\n" +
"subClassMap:\n" +
" '1':\n" +
" primInt: 1\n" +
" string: '1'\n" +
" '2':\n" +
" primInt: 2\n" +
" string: '2'\n" +
" '5':\n" +
" primInt: 5\n" +
" string: '5'\n" +
" list:\n" +
" - '5'\n" +
" set: !!set\n" +
" '5': null\n" +
" map:\n" +
" '5': 5\n" +
" testSubSubClass:\n" +
" primInt: 5\n" +
" string: '5'\n" +
" list:\n" +
" - '55'\n" +
" set: !!set\n" +
" '55': null\n" +
" map:\n" +
" '55': 5\n" +
" subClassList:\n" +
" - primInt: 500\n" +
" string: '5'\n" +
" list:\n" +
" - '55'\n" +
" set: !!set\n" +
" '55': null\n" +
" map:\n" +
" '55': 500\n" +
" subClassSet: !!set\n" +
" ? primInt: 505\n" +
" string: '5'\n" +
" list:\n" +
" - '55'\n" +
" set: !!set\n" +
" '55': null\n" +
" map:\n" +
" '55': 505\n" +
" : null\n" +
" subClassMap:\n" +
" '5':\n" +
" primInt: 510\n" +
" string: '5'\n" +
" list:\n" +
" - '55'\n" +
" set: !!set\n" +
" '55': null\n" +
" map:\n" +
" '55': 510\n" +
" '6':\n" +
" primInt: 6\n" +
" string: '6'\n" +
" list:\n" +
" - '6'\n" +
" set: !!set\n" +
" '6': null\n" +
" map:\n" +
" '6': 6\n" +
" testSubSubClass:\n" +
" primInt: 6\n" +
" string: '6'\n" +
" list:\n" +
" - '66'\n" +
" set: !!set\n" +
" '66': null\n" +
" map:\n" +
" '66': 6\n" +
" subClassList:\n" +
" - primInt: 600\n" +
" string: '6'\n" +
" list:\n" +
" - '66'\n" +
" set: !!set\n" +
" '66': null\n" +
" map:\n" +
" '66': 600\n" +
" subClassSet: !!set\n" +
" ? primInt: 606\n" +
" string: '6'\n" +
" list:\n" +
" - '66'\n" +
" set: !!set\n" +
" '66': null\n" +
" map:\n" +
" '66': 606\n" +
" : null\n" +
" subClassMap:\n" +
" '6':\n" +
" primInt: 612\n" +
" string: '6'\n" +
" list:\n" +
" - '66'\n" +
" set: !!set\n" +
" '66': null\n" +
" map:\n" +
" '66': 612\n" +
"listsList:\n" +
"- - 1\n" +
" - 2\n" +
@ -319,33 +644,297 @@ class YamlConfigurationTest {
" 2:\n" +
" '2': 2\n" +
"subClassListsList:\n" +
"- - primInt: 1\n" +
" string: '1'\n" +
" - primInt: 2\n" +
" string: '2'\n" +
"- - primInt: 7\n" +
" string: '7'\n" +
" list:\n" +
" - '7'\n" +
" set: !!set\n" +
" '7': null\n" +
" map:\n" +
" '7': 7\n" +
" testSubSubClass:\n" +
" primInt: 7\n" +
" string: '7'\n" +
" list:\n" +
" - '77'\n" +
" set: !!set\n" +
" '77': null\n" +
" map:\n" +
" '77': 7\n" +
" subClassList:\n" +
" - primInt: 700\n" +
" string: '7'\n" +
" list:\n" +
" - '77'\n" +
" set: !!set\n" +
" '77': null\n" +
" map:\n" +
" '77': 700\n" +
" subClassSet: !!set\n" +
" ? primInt: 707\n" +
" string: '7'\n" +
" list:\n" +
" - '77'\n" +
" set: !!set\n" +
" '77': null\n" +
" map:\n" +
" '77': 707\n" +
" : null\n" +
" subClassMap:\n" +
" '7':\n" +
" primInt: 714\n" +
" string: '7'\n" +
" list:\n" +
" - '77'\n" +
" set: !!set\n" +
" '77': null\n" +
" map:\n" +
" '77': 714\n" +
" - primInt: 8\n" +
" string: '8'\n" +
" list:\n" +
" - '8'\n" +
" set: !!set\n" +
" '8': null\n" +
" map:\n" +
" '8': 8\n" +
" testSubSubClass:\n" +
" primInt: 8\n" +
" string: '8'\n" +
" list:\n" +
" - '88'\n" +
" set: !!set\n" +
" '88': null\n" +
" map:\n" +
" '88': 8\n" +
" subClassList:\n" +
" - primInt: 800\n" +
" string: '8'\n" +
" list:\n" +
" - '88'\n" +
" set: !!set\n" +
" '88': null\n" +
" map:\n" +
" '88': 800\n" +
" subClassSet: !!set\n" +
" ? primInt: 808\n" +
" string: '8'\n" +
" list:\n" +
" - '88'\n" +
" set: !!set\n" +
" '88': null\n" +
" map:\n" +
" '88': 808\n" +
" : null\n" +
" subClassMap:\n" +
" '8':\n" +
" primInt: 816\n" +
" string: '8'\n" +
" list:\n" +
" - '88'\n" +
" set: !!set\n" +
" '88': null\n" +
" map:\n" +
" '88': 816\n" +
"subClassSetsSet: !!set\n" +
" ? !!set\n" +
" ? primInt: 1\n" +
" string: '1'\n" +
" ? primInt: 10\n" +
" string: '10'\n" +
" list:\n" +
" - '10'\n" +
" set: !!set\n" +
" '10': null\n" +
" map:\n" +
" '10': 10\n" +
" testSubSubClass:\n" +
" primInt: 10\n" +
" string: '10'\n" +
" list:\n" +
" - '1010'\n" +
" set: !!set\n" +
" '1010': null\n" +
" map:\n" +
" '1010': 10\n" +
" subClassList:\n" +
" - primInt: 1000\n" +
" string: '10'\n" +
" list:\n" +
" - '1010'\n" +
" set: !!set\n" +
" '1010': null\n" +
" map:\n" +
" '1010': 1000\n" +
" subClassSet: !!set\n" +
" ? primInt: 1010\n" +
" string: '10'\n" +
" list:\n" +
" - '1010'\n" +
" set: !!set\n" +
" '1010': null\n" +
" map:\n" +
" '1010': 1010\n" +
" : null\n" +
" subClassMap:\n" +
" '10':\n" +
" primInt: 1020\n" +
" string: '10'\n" +
" list:\n" +
" - '1010'\n" +
" set: !!set\n" +
" '1010': null\n" +
" map:\n" +
" '1010': 1020\n" +
" : null\n" +
" ? primInt: 2\n" +
" string: '2'\n" +
" ? primInt: 9\n" +
" string: '9'\n" +
" list:\n" +
" - '9'\n" +
" set: !!set\n" +
" '9': null\n" +
" map:\n" +
" '9': 9\n" +
" testSubSubClass:\n" +
" primInt: 9\n" +
" string: '9'\n" +
" list:\n" +
" - '99'\n" +
" set: !!set\n" +
" '99': null\n" +
" map:\n" +
" '99': 9\n" +
" subClassList:\n" +
" - primInt: 900\n" +
" string: '9'\n" +
" list:\n" +
" - '99'\n" +
" set: !!set\n" +
" '99': null\n" +
" map:\n" +
" '99': 900\n" +
" subClassSet: !!set\n" +
" ? primInt: 909\n" +
" string: '9'\n" +
" list:\n" +
" - '99'\n" +
" set: !!set\n" +
" '99': null\n" +
" map:\n" +
" '99': 909\n" +
" : null\n" +
" subClassMap:\n" +
" '9':\n" +
" primInt: 918\n" +
" string: '9'\n" +
" list:\n" +
" - '99'\n" +
" set: !!set\n" +
" '99': null\n" +
" map:\n" +
" '99': 918\n" +
" : null\n" +
" : null\n" +
"subClassMapsMap:\n" +
" 1:\n" +
" '1':\n" +
" primInt: 1\n" +
" string: '2'\n" +
" primInt: 11\n" +
" string: '11'\n" +
" list:\n" +
" - '11'\n" +
" set: !!set\n" +
" '11': null\n" +
" map:\n" +
" '11': 11\n" +
" testSubSubClass:\n" +
" primInt: 11\n" +
" string: '11'\n" +
" list:\n" +
" - '1111'\n" +
" set: !!set\n" +
" '1111': null\n" +
" map:\n" +
" '1111': 11\n" +
" subClassList:\n" +
" - primInt: 1100\n" +
" string: '11'\n" +
" list:\n" +
" - '1111'\n" +
" set: !!set\n" +
" '1111': null\n" +
" map:\n" +
" '1111': 1100\n" +
" subClassSet: !!set\n" +
" ? primInt: 1111\n" +
" string: '11'\n" +
" list:\n" +
" - '1111'\n" +
" set: !!set\n" +
" '1111': null\n" +
" map:\n" +
" '1111': 1111\n" +
" : null\n" +
" subClassMap:\n" +
" '11':\n" +
" primInt: 1122\n" +
" string: '11'\n" +
" list:\n" +
" - '1111'\n" +
" set: !!set\n" +
" '1111': null\n" +
" map:\n" +
" '1111': 1122\n" +
" 2:\n" +
" '2':\n" +
" primInt: 2\n" +
" string: '2'\n" +
" primInt: 12\n" +
" string: '12'\n" +
" list:\n" +
" - '12'\n" +
" set: !!set\n" +
" '12': null\n" +
" map:\n" +
" '12': 12\n" +
" testSubSubClass:\n" +
" primInt: 12\n" +
" string: '12'\n" +
" list:\n" +
" - '1212'\n" +
" set: !!set\n" +
" '1212': null\n" +
" map:\n" +
" '1212': 12\n" +
" subClassList:\n" +
" - primInt: 1200\n" +
" string: '12'\n" +
" list:\n" +
" - '1212'\n" +
" set: !!set\n" +
" '1212': null\n" +
" map:\n" +
" '1212': 1200\n" +
" subClassSet: !!set\n" +
" ? primInt: 1212\n" +
" string: '12'\n" +
" list:\n" +
" - '1212'\n" +
" set: !!set\n" +
" '1212': null\n" +
" map:\n" +
" '1212': 1212\n" +
" : null\n" +
" subClassMap:\n" +
" '12':\n" +
" primInt: 1224\n" +
" string: '12'\n" +
" list:\n" +
" - '1212'\n" +
" set: !!set\n" +
" '1212': null\n" +
" map:\n" +
" '1212': 1224\n" +
"e1: NON_DEFAULT\n" +
"enums:\n" +
"- DEFAULT\n" +
"- NON_DEFAULT\n" +
"converterSubClass: '2:2'\n" +
"converterSubClass: '13:13'\n" +
"excludedClass: !!de.exlll.configlib.classes.TestExcludedClass\n" +
" primInt: 1\n" +
" string: string";

@ -13,15 +13,15 @@ public final class CollectionFactory {
@SafeVarargs
public static <T> Set<T> setOf(T... values) {
return new HashSet<>(Arrays.asList(values));
return new LinkedHashSet<>(Arrays.asList(values));
}
public static <K, V> Map<K, V> mapOf() {
return new HashMap<>();
return new LinkedHashMap<>();
}
public static <K, V> Map<K, V> mapOf(K k, V v) {
HashMap<K, V> map = new HashMap<>();
HashMap<K, V> map = new LinkedHashMap<>();
map.put(k, v);
return map;
}

@ -188,7 +188,7 @@ class MyConfiguration extends YamlConfiguration {
Note: Even though sets are supported, their YAML-representation is 'pretty ugly', so it's better to use lists instead.
If you need set behavior, you can internally use lists and convert them to sets using the `preSave/postLoad`-hooks.
Lists, sets and maps that contain other types (e.g. custom types or enums) must use the `ElementType` annotation.
Lists, sets and maps that contain other types (e.g. custom types or enums) must use the `@ElementType` annotation.
Only simple types can be used as map keys.
```java
@ -208,7 +208,15 @@ class MyConfiguration extends YamlConfiguration {
}
```
Lists, sets and maps can be nested.
Lists, sets and maps can be nested. If nested collections contain custom types, you must specify the
nesting level using the `@ElementType` annotation. Examples:
* `List<T>` requires a nesting level of 0, which is the default value, so you don't have to set it
* `List<List<T>>` requires a nesting level of 1
* `List<List<List<T>>>` requires a nesting level of 2
* `List<Map<String, T>>` requires a nesting level of 1
* `List<Map<String, Map<String, T>>>` requires a nesting level of 2
```java
@ConfigurationElement
class MyCustomClass {/* fields etc.*/}
@ -218,19 +226,19 @@ class MyConfiguration extends YamlConfiguration {
private Set<Set<String>> setsSet = new HashSet<>();
private Map<Integer, Map<String, Integer>> mapsMap = new HashMap<>();
@ElementType(MyCustomClass.class)
@ElementType(value = MyCustomClass.class, nestingLevel = 1)
private List<List<MyCustomClass>> customClassListsList = new ArrayList<>();
@ElementType(MyCustomClass.class)
@ElementType(value = MyCustomClass.class, nestingLevel = 1)
private Set<Set<MyCustomClass>> customClassSetsSet = new HashSet<>();
@ElementType(MyCustomClass.class)
@ElementType(value = MyCustomClass.class, nestingLevel = 1)
private Map<Integer, Map<String, MyCustomClass>> customClassMapsMap = new HashMap<>();
// ...
}
```
#### Adding comments
You can add comments to a configuration class or a its field by using the `Comment` annotation.
You can add comments to a configuration class or a its field by using the `@Comment` annotation.
Class comments are saved at the beginning of a configuration file.
```java
@ -259,7 +267,7 @@ class MyConfiguration extends YamlConfiguration {
```
#### Excluding fields from being converted
To exclude fields from being converted, annotate them with the `NoConvert` annotation. This may be useful if the
To exclude fields from being converted, annotate them with the `@NoConvert` annotation. This may be useful if the
configuration knows how to (de-)serialize instances of that type. For example, a `BukkitYamlConfiguration` knows how
to serialize `ItemStack` instances.
@ -439,14 +447,14 @@ public final class DatabasePlugin extends JavaPlugin {
<dependency>
<groupId>de.exlll</groupId>
<artifactId>configlib-bukkit</artifactId>
<version>2.0.2</version>
<version>2.0.3</version>
</dependency>
<!-- for Bungee plugins -->
<dependency>
<groupId>de.exlll</groupId>
<artifactId>configlib-bungee</artifactId>
<version>2.0.2</version>
<version>2.0.3</version>
</dependency>
```
#### Gradle
@ -458,9 +466,9 @@ repositories {
}
dependencies {
// for Bukkit plugins
compile group: 'de.exlll', name: 'configlib-bukkit', version: '2.0.2'
compile group: 'de.exlll', name: 'configlib-bukkit', version: '2.0.3'
// for Bungee plugins
compile group: 'de.exlll', name: 'configlib-bungee', version: '2.0.2'
compile group: 'de.exlll', name: 'configlib-bungee', version: '2.0.3'
}
```

@ -1,6 +1,6 @@
allprojects {
group 'de.exlll'
version '2.0.2'
version '2.0.3'
}
subprojects {
apply plugin: 'java'

@ -186,7 +186,7 @@ final class Credentials {
private String password;
// ConfigurationElements must have a no-args constructor (can be private)
private Credentials() {}
private Credentials() { this("", ""); }
public Credentials(String username, String password) {
this.username = username;
@ -200,7 +200,7 @@ final class User {
private Credentials credentials;
private String email;
private User() {}
private User() { this("", "", ""); }
public User(String username, String password, String email) {
this.credentials = new Credentials(username, password);
@ -215,7 +215,7 @@ Now we can use the `User` class for our `moderator` field:
```java
public final class GameConfig extends BukkitYamlConfiguration {
// ...
private User moderator = new User("alex", "a@b.c", "123");
private User moderator = new User("alex", "123", "a@b.c");
// ...
}
```
@ -238,8 +238,8 @@ public final class GameConfig extends BukkitYamlConfiguration {
private Map<String, User> initUsersByName() {
Map<String, User> usersByName = new HashMap<>();
usersByName.put("Pete", new User("Pete", "pete@example.com", "123"));
usersByName.put("Mary", new User("Mary", "mary@example.com", "456"));
usersByName.put("Pete", new User("Pete", "123", "pete@example.com"));
usersByName.put("Mary", new User("Mary", "456", "mary@example.com"));
// ...
return usersByName;
}
@ -416,7 +416,7 @@ public final class GamePlugin extends JavaPlugin {
final class GameConfig extends BukkitYamlConfiguration {
@Comment("Valid color codes: &4, &c, &e")
private String winMessage = "&4YOU WON";
private User moderator = new User("alex", "a@b.c", "123");
private User moderator = new User("alex", "123", "a@b.c");
private List<String> blockedUsers = Arrays.asList("root", "john");
private List<List<String>> teamMembers = Arrays.asList(
Arrays.asList("Pete", "Mary", "Alice", "Leo"),
@ -447,16 +447,16 @@ final class GameConfig extends BukkitYamlConfiguration {
super(path, properties);
}
private HashMap<String, User> initUsersByName() {
HashMap<String, User> usersByName = new HashMap<>();
usersByName.put("Pete", new User("Pete", "pete@example.com", "123"));
usersByName.put("Mary", new User("Mary", "mary@example.com", "456"));
usersByName.put("Alice", new User("Alice", "alice@example.com", "789"));
usersByName.put("Leo", new User("Leo", "leo@example.com", "135"));
usersByName.put("Eli", new User("Eli", "eli@example.com", "246"));
usersByName.put("Eve", new User("Eve", "eve@example.com", "357"));
usersByName.put("Paul", new User("Paul", "paul@example.com", "468"));
usersByName.put("Patrick", new User("Patrick", "patrick@example.com", "579"));
private Map<String, User> initUsersByName() {
Map<String, User> usersByName = new HashMap<>();
usersByName.put("Pete", new User("Pete", "123", "pete@example.com"));
usersByName.put("Mary", new User("Mary", "456", "mary@example.com"));
usersByName.put("Alice", new User("Alice", "789", "alice@example.com"));
usersByName.put("Leo", new User("Leo", "135", "leo@example.com"));
usersByName.put("Eli", new User("Eli", "246", "eli@example.com"));
usersByName.put("Eve", new User("Eve", "357", "eve@example.com"));
usersByName.put("Paul", new User("Paul", "468", "paul@example.com"));
usersByName.put("Patrick", new User("Patrick", "579", "patrick@example.com"));
return usersByName;
}
@ -497,7 +497,7 @@ final class User {
private Credentials credentials;
private String email;
private User() {}
private User() { this("", "", ""); }
public User(String username, String password, String email) {
this.credentials = new Credentials(username, password);
@ -511,7 +511,7 @@ final class Credentials {
private String password;
// ConfigurationElements must have a no-args constructor (can be private)
private Credentials() {}
private Credentials() { this("", ""); }
public Credentials(String username, String password) {
this.username = username;

Loading…
Cancel
Save