support for lists, sets and maps of custom objects

dev
Exlll 8 years ago
parent 0e9b474db1
commit 6b72d6293c

@ -1,5 +1,5 @@
name: ConfigLib
author: Exlll
version: 1.2.0
version: 1.3.0-SNAPSHOT
main: de.exlll.configlib.ConfigLib

@ -1,5 +1,5 @@
name: ConfigLib
author: Exlll
version: 1.2.0
version: 1.3.0-SNAPSHOT
main: de.exlll.configlib.ConfigLib

@ -0,0 +1,203 @@
package de.exlll.configlib;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
public final class ConfigList<T> implements Defaultable<List<?>>, List<T>, RandomAccess {
private final Class<T> cls;
private final List<T> list;
public ConfigList(Class<T> cls) {
Objects.requireNonNull(cls);
if (!Reflect.isSimpleType(cls)) {
Reflect.checkDefaultConstructor(cls);
}
this.cls = cls;
this.list = Collections.checkedList(new ArrayList<>(), cls);
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public boolean contains(Object o) {
return list.contains(o);
}
@Override
public Iterator<T> iterator() {
return list.iterator();
}
@Override
public Object[] toArray() {
return list.toArray();
}
@Override
public <T1> T1[] toArray(T1[] a) {
return list.toArray(a);
}
@Override
public boolean add(T t) {
return list.add(t);
}
@Override
public boolean remove(Object o) {
return list.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return list.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends T> c) {
return list.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends T> c) {
return list.addAll(index, c);
}
@Override
public boolean removeAll(Collection<?> c) {
return list.removeAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return list.retainAll(c);
}
@Override
public void clear() {
list.clear();
}
@Override
public T get(int index) {
return list.get(index);
}
@Override
public T set(int index, T element) {
return list.set(index, element);
}
@Override
public void add(int index, T element) {
list.add(index, element);
}
@Override
public T remove(int index) {
return list.remove(index);
}
@Override
public int indexOf(Object o) {
return list.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return list.lastIndexOf(o);
}
@Override
public ListIterator<T> listIterator() {
return list.listIterator();
}
@Override
public ListIterator<T> listIterator(int index) {
return list.listIterator(index);
}
@Override
public List<T> subList(int fromIndex, int toIndex) {
return list.subList(fromIndex, toIndex);
}
@Override
public void replaceAll(UnaryOperator<T> operator) {
list.replaceAll(operator);
}
@Override
public void sort(Comparator<? super T> c) {
list.sort(c);
}
@Override
public Spliterator<T> spliterator() {
return list.spliterator();
}
@Override
public boolean removeIf(Predicate<? super T> filter) {
return list.removeIf(filter);
}
@Override
public Stream<T> stream() {
return list.stream();
}
@Override
public Stream<T> parallelStream() {
return list.parallelStream();
}
@Override
public void forEach(Consumer<? super T> action) {
list.forEach(action);
}
@Override
public String toString() {
return "ConfigList{" +
"cls=" + cls +
", list=" + list +
'}';
}
@Override
public List<?> toDefault() {
if (Reflect.isSimpleType(cls)) {
return new ArrayList<>(list);
}
List<Object> l = new ArrayList<>();
for (Object item : list) {
l.add(FieldMapper.instanceToMap(item));
}
return l;
}
@Override
public void fromDefault(Object value) {
for (Object item : (List<?>) value) {
Object instance = fromDefault(item, cls);
add(cls.cast(instance));
}
}
List<T> getList() {
return list;
}
}

@ -0,0 +1,183 @@
package de.exlll.configlib;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
public final class ConfigMap<K, V> implements Defaultable<Map<K, ?>>, Map<K, V> {
private final Class<K> keyClass;
private final Class<V> valueClass;
private final Map<K, V> map;
public ConfigMap(Class<K> keyClass, Class<V> valueClass) {
Objects.requireNonNull(keyClass);
Objects.requireNonNull(valueClass);
checkSimpleTypeKey(keyClass);
if (!Reflect.isSimpleType(valueClass)) {
Reflect.checkDefaultConstructor(valueClass);
}
this.keyClass = keyClass;
this.valueClass = valueClass;
this.map = Collections.checkedMap(
new HashMap<>(), keyClass, valueClass
);
}
private void checkSimpleTypeKey(Class<?> keyClass) {
if (!Reflect.isSimpleType(keyClass)) {
String msg = "Class " + keyClass.getSimpleName() + " is not a simple type.\n" +
"Only simple types can be used as keys in a map.";
throw new IllegalArgumentException(msg);
}
}
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public V get(Object key) {
return map.get(key);
}
@Override
public V put(K key, V value) {
return map.put(key, value);
}
@Override
public V remove(Object key) {
return map.remove(key);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
map.putAll(m);
}
@Override
public void clear() {
map.clear();
}
@Override
public Set<K> keySet() {
return map.keySet();
}
@Override
public Collection<V> values() {
return map.values();
}
@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
@Override
public V getOrDefault(Object key, V defaultValue) {
return map.getOrDefault(key, defaultValue);
}
@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
map.forEach(action);
}
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
map.replaceAll(function);
}
@Override
public V putIfAbsent(K key, V value) {
return map.putIfAbsent(key, value);
}
@Override
public boolean remove(Object key, Object value) {
return map.remove(key, value);
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
return map.replace(key, oldValue, newValue);
}
@Override
public V replace(K key, V value) {
return map.replace(key, value);
}
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
return map.computeIfAbsent(key, mappingFunction);
}
@Override
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return map.computeIfPresent(key, remappingFunction);
}
@Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return map.compute(key, remappingFunction);
}
@Override
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
return map.merge(key, value, remappingFunction);
}
@Override
public String toString() {
return "ConfigMap{" +
"valueClass=" + valueClass +
", map=" + map +
'}';
}
@Override
public Map<K, ?> toDefault() {
if (Reflect.isSimpleType(valueClass)) {
return new LinkedHashMap<>(map);
}
Map<K, Object> m = new LinkedHashMap<>();
for (Entry<K, V> entry : entrySet()) {
Object mapped = FieldMapper.instanceToMap(entry.getValue());
m.put(entry.getKey(), mapped);
}
return m;
}
@Override
public void fromDefault(Object value) {
for (Map.Entry<?, ?> entry : ((Map<?, ?>) value).entrySet()) {
Object instance = fromDefault(entry.getValue(), valueClass);
Reflect.checkType(entry.getKey(), keyClass);
put(keyClass.cast(entry.getKey()), valueClass.cast(instance));
}
}
Map<K, V> getMap() {
return map;
}
}

@ -0,0 +1,142 @@
package de.exlll.configlib;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
public final class ConfigSet<T> implements Defaultable<Set<?>>, Set<T> {
private final Class<T> cls;
private final Set<T> set;
public ConfigSet(Class<T> cls) {
Objects.requireNonNull(cls);
if (!Reflect.isSimpleType(cls)) {
Reflect.checkDefaultConstructor(cls);
}
this.cls = cls;
this.set = Collections.checkedSet(new HashSet<>(), cls);
}
@Override
public int size() {
return set.size();
}
@Override
public boolean isEmpty() {
return set.isEmpty();
}
@Override
public boolean contains(Object o) {
return set.contains(o);
}
@Override
public Iterator<T> iterator() {
return set.iterator();
}
@Override
public void forEach(Consumer<? super T> action) {
set.forEach(action);
}
@Override
public Object[] toArray() {
return set.toArray();
}
@Override
public <T1> T1[] toArray(T1[] a) {
return set.toArray(a);
}
@Override
public boolean add(T t) {
return set.add(t);
}
@Override
public boolean remove(Object o) {
return set.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return set.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends T> c) {
return set.addAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return set.retainAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
return set.removeAll(c);
}
@Override
public boolean removeIf(Predicate<? super T> filter) {
return set.removeIf(filter);
}
@Override
public void clear() {
set.clear();
}
@Override
public Spliterator<T> spliterator() {
return set.spliterator();
}
@Override
public Stream<T> stream() {
return set.stream();
}
@Override
public Stream<T> parallelStream() {
return set.parallelStream();
}
@Override
public String toString() {
return "ConfigSet{" +
"cls=" + cls +
", set=" + set +
'}';
}
@Override
public Set<?> toDefault() {
if (Reflect.isSimpleType(cls)) {
return new LinkedHashSet<>(set);
}
Set<Object> s = new LinkedHashSet<>();
for (Object item : set) {
s.add(FieldMapper.instanceToMap(item));
}
return s;
}
@Override
public void fromDefault(Object value) {
for (Object item : (Set<?>) value) {
Object instance = fromDefault(item, cls);
add(cls.cast(instance));
}
}
Set<T> getSet() {
return set;
}
}

@ -0,0 +1,23 @@
package de.exlll.configlib;
import java.util.Map;
interface Defaultable<T> {
T toDefault();
void fromDefault(Object value);
default Object fromDefault(final Object instance, Class<?> cls) {
Object newInstance = instance;
if (!Reflect.isSimpleType(cls)) {
newInstance = Reflect.newInstance(cls);
Reflect.checkType(instance, Map.class);
Reflect.checkMapEntries((Map<?, ?>) instance, String.class, Object.class);
@SuppressWarnings("unchecked")
Map<String, ?> map = (Map<String, ?>) instance;
FieldMapper.instanceFromMap(newInstance, map);
}
Reflect.checkType(newInstance, cls);
return newInstance;
}
}

@ -2,19 +2,10 @@ package de.exlll.configlib;
import java.lang.reflect.Field;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
enum FieldMapper {
;
private static final Set<Class<?>> defaultClasses = Stream.of(
Boolean.class, String.class, Character.class,
Byte.class, Short.class, Integer.class, Long.class,
Float.class, Double.class
).collect(Collectors.toSet());
static Map<String, Object> instanceToMap(Object instance) {
Map<String, Object> map = new LinkedHashMap<>();
@ -22,29 +13,46 @@ enum FieldMapper {
FilteredFields ff = FilteredFields.of(instance.getClass());
for (Field field : ff) {
Object value = getValue(field, instance);
Object value = Reflect.getValue(field, instance);
checkNull(field, value);
value = isDefault(field.getType()) ? value : instanceToMap(value);
value = toSerializableObject(value);
map.put(field.getName(), value);
}
return map;
}
static Object toSerializableObject(Object input) {
if (input instanceof Defaultable<?>) {
return ((Defaultable<?>) input).toDefault();
} else if (Reflect.isDefault(input.getClass())) {
return input;
} else {
return instanceToMap(input);
}
}
static void instanceFromMap(Object instance, Map<String, ?> map) {
FilteredFields ff = FilteredFields.of(instance.getClass());
for (Field field : ff) {
Object val = map.get(field.getName());
if (val == null) {
continue; // keep default value
}
if (isDefault(field.getType())) {
setValue(field, instance, val);
} else {
Object inst = getValue(field, instance);
instanceFromMap(inst, castToMap(val));
}
Object value = map.get(field.getName());
fromSerializedObject(field, instance, value);
}
}
static void fromSerializedObject(Field field, Object instance, Object serialized) {
if (serialized == null) {
return; // keep default value
}
if (instance instanceof Defaultable<?>) {
instance = Reflect.getValue(field, instance);
((Defaultable<?>) instance).fromDefault(serialized);
} else if (Reflect.isDefault(field.getType())) {
Reflect.setValue(field, instance, serialized);
} else {
instance = Reflect.getValue(field, instance);
instanceFromMap(instance, castToMap(serialized));
}
}
@ -57,46 +65,11 @@ enum FieldMapper {
}
private static Map<String, Object> castToMap(Object map) {
private static Map<String, Object> castToMap(Object mapObject) {
Reflect.checkType(mapObject, Map.class);
Reflect.checkMapEntries((Map<?, ?>) mapObject, String.class, Object.class);
@SuppressWarnings("unchecked")
Map<String, Object> m = (Map<String, Object>) map;
Map<String, Object> m = (Map<String, Object>) mapObject;
return m;
}
private static boolean isDefault(Class<?> cls) {
// all default types are properly handled by the YamlSerializer
return cls.isPrimitive() || isDefaultClass(cls) || isDefaultType(cls);
}
private static boolean isDefaultClass(Class<?> cls) {
return defaultClasses.contains(cls);
}
private static boolean isDefaultType(Class<?> cls) {
return Map.class.isAssignableFrom(cls) ||
Set.class.isAssignableFrom(cls) ||
List.class.isAssignableFrom(cls);
}
static Object getValue(Field field, Object instance) {
try {
field.setAccessible(true);
return field.get(instance);
} catch (IllegalAccessException e) {
/* This exception is never thrown because we filter
* inaccessible fields out. */
throw new RuntimeException(e);
}
}
static void setValue(Field field, Object instance, Object value) {
try {
field.setAccessible(true);
field.set(instance, value);
} catch (IllegalAccessException e) {
/* This exception is never thrown because we filter
* inaccessible fields out. */
throw new RuntimeException(e);
}
}
}

@ -0,0 +1,119 @@
package de.exlll.configlib;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
enum Reflect {
;
private static final Class<?>[] SIMPLE_TYPES = {
Boolean.class, String.class, Character.class,
Byte.class, Short.class, Integer.class, Long.class,
Float.class, Double.class
};
private static final Set<Class<?>> simpleTypes = Collections.unmodifiableSet(
new HashSet<>(Arrays.asList(SIMPLE_TYPES))
);
static boolean isDefault(Class<?> cls) {
// default classes can be properly serialized by default
return isSimpleType(cls) || isContainerType(cls);
}
static boolean isSimpleType(Class<?> cls) {
return cls.isPrimitive() || simpleTypes.contains(cls);
}
static boolean isConfigList(Class<?> cls) {
return ConfigList.class.isAssignableFrom(cls);
}
static boolean isConfigSet(Class<?> cls) {
return ConfigSet.class.isAssignableFrom(cls);
}
static boolean isConfigMap(Class<?> cls) {
return ConfigMap.class.isAssignableFrom(cls);
}
static boolean isContainerType(Class<?> cls) {
return List.class.isAssignableFrom(cls) ||
Set.class.isAssignableFrom(cls) ||
Map.class.isAssignableFrom(cls);
}
static Object newInstance(Class<?> cls) {
checkDefaultConstructor(cls);
Constructor<?> constructor = getDefaultConstructor(cls);
constructor.setAccessible(true);
return newInstance(constructor);
}
private static Object newInstance(Constructor<?> constructor) {
try {
return constructor.newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
static void checkDefaultConstructor(Class<?> cls) {
if (!hasDefaultConstructor(cls)) {
String msg = "Class " + cls.getSimpleName() + " doesn't have a default constructor.";
throw new IllegalArgumentException(msg);
}
}
static Constructor<?> getDefaultConstructor(Class<?> cls) {
try {
return cls.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
static boolean hasDefaultConstructor(Class<?> cls) {
return Arrays.stream(cls.getDeclaredConstructors())
.anyMatch(cst -> cst.getParameterCount() == 0);
}
static Object getValue(Field field, Object instance) {
try {
field.setAccessible(true);
return field.get(instance);
} catch (IllegalAccessException e) {
/* This exception is never thrown because we filter
* inaccessible fields out. */
throw new RuntimeException(e);
}
}
static void setValue(Field field, Object instance, Object value) {
try {
field.setAccessible(true);
field.set(instance, value);
} catch (IllegalAccessException e) {
/* This exception is never thrown because we filter
* inaccessible fields out. */
throw new RuntimeException(e);
}
}
static void checkType(Object object, Class<?> expectedType) {
if (!expectedType.isAssignableFrom(object.getClass())) {
String clsName = object.getClass().getSimpleName();
String msg = "Invalid type!\n" +
"Object '" + object + "' is of type " + clsName + ". " +
"Expected type: " + expectedType.getSimpleName();
throw new IllegalArgumentException(msg);
}
}
static void checkMapEntries(Map<?, ?> map, Class<?> keyClass, Class<?> valueClass) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
checkType(entry.getKey(), keyClass);
checkType(entry.getValue(), valueClass);
}
}
}

@ -8,9 +8,13 @@ import org.junit.runners.Suite;
ConfigReadWriteTest.class,
CommentAdderTest.class,
CommentsTest.class,
ConfigListTest.class,
ConfigSetTest.class,
ConfigMapTest.class,
FieldFilterTest.class,
FieldMapperTest.class,
FilteredFieldsTest.class,
ReflectTest.class,
YamlSerializerTest.class
})
public class ConfigLibTestSuite {

@ -0,0 +1,82 @@
package de.exlll.configlib;
import de.exlll.configlib.classes.ConfigTypeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static de.exlll.configlib.classes.ConfigTypeClass.A;
import static de.exlll.configlib.classes.ConfigTypeClass.from;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class ConfigListTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void constructorRequiresNonNullClass() throws Exception {
expectedException.expect(NullPointerException.class);
new ConfigList<>(null);
}
@Test
public void constructorRequiresClassWithDefaultConstructor() throws Exception {
new ConfigList<>(String.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Class ConfigList doesn't have a default constructor.");
new ConfigList<>(ConfigList.class);
}
@Test
public void toDefaultReturnsArrayList() throws Exception {
List<?> list = new ConfigList<>(String.class).toDefault();
assertThat(list, instanceOf(ArrayList.class));
}
@Test
public void toDefaultReturnsSimpleTypes() throws Exception {
ConfigTypeClass c = new ConfigTypeClass();
List<?> l = c.configListSimple.toDefault();
assertThat(l, is(c.configListSimple.getList()));
}
@Test
public void toDefaultReturnsSerializedObjects() throws Exception {
ConfigTypeClass c = new ConfigTypeClass();
List<?> l = c.configList.toDefault();
for (int i = 0; i < c.configList.size(); i++) {
Object mapped = FieldMapper.instanceToMap(c.configList.get(i));
assertThat(mapped, is(l.get(i)));
}
}
@Test
public void fromDefaultAddsSimpleTypes() throws Exception {
List<String> list = Arrays.asList("a", "b");
ConfigList<String> configList = new ConfigList<>(String.class);
configList.fromDefault(list);
assertThat(configList.getList(), is(list));
}
@Test
public void fromDefaultAddsDeserializedObjects() throws Exception {
A a = from("a");
A b = from("b");
Map<String, ?> map1 = FieldMapper.instanceToMap(a);
Map<String, ?> map2 = FieldMapper.instanceToMap(b);
List<Map<String, ?>> list = Arrays.asList(map1, map2);
ConfigList<A> configList = new ConfigList<>(A.class);
configList.fromDefault(list);
assertThat(configList.getList(), is(Arrays.asList(a, b)));
}
}

@ -0,0 +1,103 @@
package de.exlll.configlib;
import de.exlll.configlib.classes.ConfigTypeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.*;
import static de.exlll.configlib.classes.ConfigTypeClass.*;
import static de.exlll.configlib.classes.ConfigTypeClass.from;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class ConfigMapTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void constructorRequiresNonNullKeyClass() throws Exception {
expectedException.expect(NullPointerException.class);
new ConfigMap<>(null, Integer.class);
}
@Test
public void constructorRequiresNonNullValueClass() throws Exception {
expectedException.expect(NullPointerException.class);
new ConfigMap<>(String.class, null);
}
@Test
public void constructorRequiresValueClassWithDefaultConstructor() throws Exception {
new ConfigMap<>(String.class, String.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Class ConfigMap doesn't have a default constructor.");
new ConfigMap<>(String.class, ConfigMap.class);
}
@Test
public void constructorRequiresKeyClassWithSimpleType() throws Exception {
new ConfigMap<>(String.class, ConfigListTest.class);
String msg = "Class " + Map.class.getSimpleName() + " is not a simple type.\n" +
"Only simple types can be used as keys in a map.";
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(msg);
new ConfigMap<>(Map.class, ConfigListTest.class);
}
@Test
public void toDefaultReturnsLinkedHashMap() throws Exception {
Map<String, ?> newMap = new ConfigMap<>(String.class, Integer.class).toDefault();
assertThat(newMap, instanceOf(LinkedHashMap.class));
}
@Test
public void toDefaultReturnsSimpleTypes() throws Exception {
ConfigTypeClass c = new ConfigTypeClass();
Map<String, ?> m = c.configMapSimple.toDefault();
assertThat(m, is(c.configMapSimple.getMap()));
}
@Test
public void toDefaultReturnsSerializedObjects() throws Exception {
ConfigTypeClass c = new ConfigTypeClass();
Map<String, ?> s = c.configMap.toDefault();
for (Map.Entry<String, ConfigTypeClass.A> entry : c.configMap.entrySet()) {
Object mapped = FieldMapper.instanceToMap(entry.getValue());
assertThat(mapped, is(s.get(entry.getKey())));
}
}
@Test
public void fromDefaultAddsSimpleTypes() throws Exception {
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
ConfigMap<String, Integer> configMap = new ConfigMap<>(String.class, Integer.class);
configMap.fromDefault(map);
assertThat(configMap.getMap(), is(map));
}
@Test
public void fromDefaultAddsDeserializedObjects() throws Exception {
A a = from("a");
A b = from("b");
Map<String, ?> map1 = FieldMapper.instanceToMap(a);
Map<String, ?> map2 = FieldMapper.instanceToMap(b);
Map<String, Map<String, ?>> map = new HashMap<>();
map.put("a", map1);
map.put("b", map2);
ConfigMap<String, A> configMap = new ConfigMap<>(String.class, A.class);
configMap.fromDefault(map);
Map<String, A> newMap = new HashMap<>();
newMap.put("a", a);
newMap.put("b", b);
assertThat(configMap.getMap(), is(newMap));
}
}

@ -0,0 +1,78 @@
package de.exlll.configlib;
import de.exlll.configlib.classes.ConfigTypeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.*;
import static de.exlll.configlib.classes.ConfigTypeClass.*;
import static de.exlll.configlib.classes.ConfigTypeClass.from;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
public class ConfigSetTest {
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void constructorRequiresNonNullClass() throws Exception {
expectedException.expect(NullPointerException.class);
new ConfigSet<>(null);
}
@Test
public void constructorRequiresClassWithDefaultConstructor() throws Exception {
new ConfigSet<>(String.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Class ConfigSet doesn't have a default constructor.");
new ConfigSet<>(ConfigSet.class);
}
@Test
public void toDefaultReturnsLinkedHashSet() throws Exception {
Set<?> set = new ConfigSet<>(String.class).toDefault();
assertThat(set, instanceOf(LinkedHashSet.class));
}
@Test
public void toDefaultReturnsSimpleTypes() throws Exception {
ConfigTypeClass c = new ConfigTypeClass();
Set<?> s = c.configSetSimple.toDefault();
assertThat(s, is(c.configSetSimple.getSet()));
}
@Test
public void toDefaultReturnsSerializedObjects() throws Exception {
ConfigTypeClass c = new ConfigTypeClass();
Set<?> s = c.configSet.toDefault();
for (A a : c.configSet) {
Object mapped = FieldMapper.instanceToMap(a);
assertThat(s, contains(mapped));
}
}
@Test
public void fromDefaultAddsSimpleTypes() throws Exception {
Set<String> set = new HashSet<>(Arrays.asList("a", "b"));
ConfigSet<String> configSet = new ConfigSet<>(String.class);
configSet.fromDefault(set);
assertThat(configSet.getSet(), is(set));
}
@Test
public void fromDefaultAddsDeserializedObjects() throws Exception {
A a = from("a");
A b = from("b");
Map<String, ?> map1 = FieldMapper.instanceToMap(a);
Map<String, ?> map2 = FieldMapper.instanceToMap(b);
Set<Map<String, ?>> set = new HashSet<>(Arrays.asList(map1, map2));
ConfigSet<A> configSet = new ConfigSet<>(A.class);
configSet.fromDefault(set);
assertThat(configSet.getSet(), is(new HashSet<>(Arrays.asList(a, b))));
}
}

@ -1,5 +1,7 @@
package de.exlll.configlib;
import de.exlll.configlib.classes.DefaultTypeClass;
import de.exlll.configlib.classes.NonDefaultTypeClass;
import org.junit.Test;
import java.lang.reflect.Field;
@ -12,6 +14,81 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
public class FieldMapperTest {
@Test
public void toSerializableObjectReturnsObjectForDefaultTypes() throws Exception {
DefaultTypeClass instance = new DefaultTypeClass();
for (Field f : DefaultTypeClass.class.getDeclaredFields()) {
Object value = Reflect.getValue(f, instance);
assertThat(FieldMapper.toSerializableObject(value), sameInstance(value));
}
}
@Test
public void toSerializableObjectReturnsMapForNonDefaultTypes() throws Exception {
DefaultTypeClass instance = new DefaultTypeClass();
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) FieldMapper.toSerializableObject(instance);
int counter = 0;
for (Field field : DefaultTypeClass.class.getDeclaredFields()) {
Object fieldValue = Reflect.getValue(field, instance);
assertThat(map.get(field.getName()), is(fieldValue));
counter++;
}
assertThat(map.size(), is(counter));
}
@Test
public void fromSerializedObjectIgnoresNullValues() throws Exception {
DefaultTypeClass instance = new DefaultTypeClass();
for (Field field : DefaultTypeClass.class.getDeclaredFields()) {
Object currentValue = Reflect.getValue(field, instance);
FieldMapper.fromSerializedObject(field, instance, null);
Object newValue = Reflect.getValue(field, instance);
if (field.getType().isPrimitive()) {
assertThat(currentValue, is(newValue));
} else {
assertThat(currentValue, sameInstance(newValue));
}
}
}
@Test
public void fromSerializedObjectSetsValueIfDefaultType() throws Exception {
DefaultTypeClass instance = new DefaultTypeClass();
Map<String, Object> map = DefaultTypeClass.newValues();
for (Field field : DefaultTypeClass.class.getDeclaredFields()) {
String fieldName = field.getName();
Object mapValue = map.get(fieldName);
FieldMapper.fromSerializedObject(field, instance, mapValue);
Object value = Reflect.getValue(field, instance);
if (field.getType().isPrimitive()) {
assertThat(mapValue, is(value));
} else {
assertThat(mapValue, sameInstance(value));
}
}
}
@Test
public void fromSerializedObjectUpdatesValueIfNotDefaultType() throws Exception {
NonDefaultTypeClass instance = new NonDefaultTypeClass();
Field field = NonDefaultTypeClass.class.getDeclaredField("defaultTypeClass");
Map<String, Object> map = DefaultTypeClass.newValues();
FieldMapper.fromSerializedObject(field, instance, map);
for (Map.Entry<String, Object> entry : map.entrySet()) {
Field f = DefaultTypeClass.class.getDeclaredField(entry.getKey());
Object value = Reflect.getValue(f, instance.defaultTypeClass);
assertThat(value, is(entry.getValue()));
}
}
@Test
public void instanceTopMapCreatesMap() throws Exception {
@ -93,23 +170,6 @@ public class FieldMapperTest {
assertThat(t.b.t, is("v"));
}
@Test
public void getValueGetsValue() throws Exception {
TestClass testClass = new TestClass();
Field s = TestClass.class.getDeclaredField("s");
assertThat(FieldMapper.getValue(s, testClass), is("s"));
}
@Test
public void setValueSetsValue() throws Exception {
TestClass testClass = new TestClass();
Field s = TestClass.class.getDeclaredField("s");
FieldMapper.setValue(s, testClass, "t");
assertThat(testClass.s, is("t"));
}
private static final class TestClass {
private int z;
private int i = 1;

@ -0,0 +1,172 @@
package de.exlll.configlib;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
public class ReflectTest {
private static final Class<TestClass> cls1 = TestClass.class;
private static final Class<NotDefaultConstructor> cls2 = NotDefaultConstructor.class;
private final List<String> list = new ConfigList<>(String.class);
private final Set<String> set = new ConfigSet<>(String.class);
private final Map<?, String> map = new ConfigMap<>(String.class, String.class);
private final Class<?>[] containerClasses = {List.class, Set.class, Map.class};
private final Class<?>[] simpleClasses = {
boolean.class, char.class, byte.class, short.class,
int.class, long.class, float.class, double.class,
Boolean.class, String.class, Character.class,
Byte.class, Short.class, Integer.class, Long.class,
Float.class, Double.class,
};
private final String errorMessage = "Class NotDefaultConstructor doesn't have a default constructor.";
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Test
public void checkType() throws Exception {
Reflect.checkType(new HashMap<>(), Map.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Invalid type!\n" +
"Object 'a' is of type String. Expected type: Map");
Reflect.checkType("a", Map.class);
}
@Test
public void checkMapEntriesChecksKeys() throws Exception {
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
Reflect.checkMapEntries(map, String.class, Integer.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Invalid type!\n" +
"Object 'a' is of type String. Expected type: Integer");
Reflect.checkMapEntries(map, Integer.class, Integer.class);
}
@Test
public void checkMapEntriesChecksValues() throws Exception {
Map<String, Integer> map = new HashMap<>();
map.put("a", 1);
Reflect.checkMapEntries(map, String.class, Integer.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Invalid type!\n" +
"Object '1' is of type Integer. Expected type: String");
Reflect.checkMapEntries(map, String.class, String.class);
}
@Test
public void isDefault() throws Exception {
for (Class<?> cls : containerClasses) {
assertThat(Reflect.isDefault(cls), is(true));
}
for (Class<?> cls : simpleClasses) {
assertThat(Reflect.isDefault(cls), is(true));
}
}
@Test
public void isSimpleType() throws Exception {
for (Class<?> cls : simpleClasses) {
assertThat(Reflect.isSimpleType(cls), is(true));
}
}
@Test
public void isConfigList() throws Exception {
assertThat(Reflect.isConfigList(list.getClass()), is(true));
assertThat(Reflect.isConfigList(set.getClass()), is(false));
assertThat(Reflect.isConfigList(map.getClass()), is(false));
}
@Test
public void isConfigSet() throws Exception {
assertThat(Reflect.isConfigSet(list.getClass()), is(false));
assertThat(Reflect.isConfigSet(set.getClass()), is(true));
assertThat(Reflect.isConfigSet(map.getClass()), is(false));
}
@Test
public void isConfigMap() throws Exception {
assertThat(Reflect.isConfigMap(list.getClass()), is(false));
assertThat(Reflect.isConfigMap(set.getClass()), is(false));
assertThat(Reflect.isConfigMap(map.getClass()), is(true));
}
@Test
public void isContainerType() throws Exception {
assertThat(Reflect.isContainerType(list.getClass()), is(true));
assertThat(Reflect.isContainerType(set.getClass()), is(true));
assertThat(Reflect.isContainerType(map.getClass()), is(true));
assertThat(Reflect.isContainerType(Object.class), is(false));
}
@Test
public void getValueGetsValue() throws Exception {
TestClass testClass = new TestClass();
Field s = TestClass.class.getDeclaredField("s");
assertThat(Reflect.getValue(s, testClass), is("s"));
}
@Test
public void setValueSetsValue() throws Exception {
TestClass testClass = new TestClass();
Field s = TestClass.class.getDeclaredField("s");
Reflect.setValue(s, testClass, "t");
assertThat(testClass.s, is("t"));
}
@Test
public void checkForDefaultConstructorsThrowsExceptionIfNoDefault() throws Exception {
Reflect.checkDefaultConstructor(cls1);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(errorMessage);
Reflect.checkDefaultConstructor(cls2);
}
@Test
public void hasDefaultConstructor() throws Exception {
assertThat(Reflect.hasDefaultConstructor(cls1), is(true));
assertThat(Reflect.hasDefaultConstructor(cls2), is(false));
}
@Test
public void getDefaultConstructor() throws Exception {
assertThat(Reflect.getDefaultConstructor(cls1), is(cls1.getDeclaredConstructor()));
expectedException.expect(RuntimeException.class);
Reflect.getDefaultConstructor(cls2);
}
@Test
public void newInstanceChecksForDefaultConstructor() throws Exception {
Reflect.newInstance(TestClass.class);
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage(errorMessage);
Reflect.newInstance(NotDefaultConstructor.class);
}
@Test
public void newInstanceCreatesNewInstance() throws Exception {
TestClass t = (TestClass) Reflect.newInstance(TestClass.class);
}
private static final class NotDefaultConstructor {
public NotDefaultConstructor(String a) {
}
}
private static final class TestClass {
private String s = "s";
}
}

@ -0,0 +1,87 @@
package de.exlll.configlib.classes;
import de.exlll.configlib.ConfigList;
import de.exlll.configlib.ConfigMap;
import de.exlll.configlib.ConfigSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class ConfigTypeClass {
public ConfigList<String> configListSimple = new ConfigList<>(String.class);
public ConfigSet<String> configSetSimple = new ConfigSet<>(String.class);
public ConfigMap<String, String> configMapSimple = new ConfigMap<>(String.class, String.class);
public ConfigList<A> configList = new ConfigList<>(A.class);
public ConfigSet<A> configSet = new ConfigSet<>(A.class);
public ConfigMap<String, A> configMap = new ConfigMap<>(String.class, A.class);
public ConfigTypeClass() {
configListSimple.add("a");
configSetSimple.add("b");
configMapSimple.put("c", "d");
configList.add(from("e"));
configSet.add(from("f"));
configMap.put("g", from("h"));
}
public static Map<String, Object> newValues() {
Map<String, Object> map = new HashMap<>();
List<String> configListSimple = new ConfigList<>(String.class);
configListSimple.add("b");
Set<String> configSetSimple = new ConfigSet<>(String.class);
configSetSimple.add("c");
Map<String, String> configMapSimple = new ConfigMap<>(String.class, String.class);
configMapSimple.put("d", "e");
List<A> configList = new ConfigList<>(A.class);
configList.add(from("f"));
Set<A> configSet = new ConfigSet<>(A.class);
configSet.add(from("g"));
Map<String, A> configMap = new ConfigMap<>(String.class, A.class);
configMap.put("h", from("i"));
map.put("configListSimple", configListSimple);
map.put("configSetSimple", configSetSimple);
map.put("configMapSimple", configMapSimple);
map.put("configList", configList);
map.put("configSet", configSet);
map.put("configMap", configMap);
return map;
}
public static A from(String string) {
A a = new A();
a.string = string;
return a;
}
public static final class A {
private String string = "string";
@Override
public String toString() {
return "A{" +
"string='" + string + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
A a = (A) o;
return string.equals(a.string);
}
@Override
public int hashCode() {
return string.hashCode();
}
}
}

@ -0,0 +1,63 @@
package de.exlll.configlib.classes;
import java.util.*;
public class DefaultTypeClass {
private boolean bool = true;
private char c = 'c';
private byte b = 1;
private short s = 2;
private int i = 3;
private long l = 4;
private float f = 5.0f;
private double d = 6.0;
private Boolean boolObject = true;
private Character cObject = 'c';
private Byte bObject = 1;
private Short sObject = 2;
private Integer iObject = 3;
private Long lObject = 4L;
private Float fObject = 5.0f;
private Double dObject = 6.0;
private String string = "string";
private List<String> list = new ArrayList<>();
private Set<String> set = new HashSet<>();
private Map<String, String> map = new HashMap<>();
public DefaultTypeClass() {
list.add("a");
set.add("b");
map.put("c", "d");
}
public static Map<String, Object> newValues() {
Map<String, Object> map = new HashMap<>();
map.put("bool", false);
map.put("c", 'd');
map.put("b", (byte) 2);
map.put("s", (short) 3);
map.put("i", 4);
map.put("l", 5L);
map.put("f", 6.0f);
map.put("d", 7.0);
map.put("boolObject", false);
map.put("cObject", 'd');
map.put("bObject", (byte) 2);
map.put("sObject", (short) 3);
map.put("iObject", 4);
map.put("lObject", 5L);
map.put("fObject", 6.0f);
map.put("dObject", 7.0);
map.put("string", "string2");
List<String> list = new ArrayList<>();
list.add("b");
map.put("list", list);
Set<String> set = new HashSet<>();
set.add("c");
map.put("set", set);
Map<String, String> map2 = new HashMap<>();
map2.put("d", "e");
map.put("map", map2);
return map;
}
}

@ -0,0 +1,5 @@
package de.exlll.configlib.classes;
public class NonDefaultTypeClass {
public DefaultTypeClass defaultTypeClass = new DefaultTypeClass();
}

@ -0,0 +1,21 @@
package de.exlll.configlib.classes;
public class SimpleTypesClass {
private boolean bool = true;
private char c = 'c';
private byte b = 1;
private short s = 2;
private int i = 3;
private long l = 4;
private float f = 5.0f;
private double d = 6.0;
private Boolean boolObject = true;
private Character cObject = 'c';
private Byte bObject = 1;
private Short sObject = 2;
private Integer iObject = 3;
private Long lObject = 4L;
private Float fObject = 5.0f;
private Double dObject = 6.0;
private String string = "string";
}

@ -122,36 +122,36 @@ public class ExamplePlugin extends JavaPlugin {
```xml
<repository>
<id>de.exlll</id>
<url>https://repo.exlll.de/artifactory/releases/</url>
<url>https://repo.exlll.de/artifactory/snapshots/</url>
</repository>
<!-- for Bukkit plugins -->
<dependency>
<groupId>de.exlll</groupId>
<artifactId>configlib-bukkit</artifactId>
<version>1.2.0</version>
<version>1.3.0-SNAPSHOT</version>
</dependency>
<!-- for Bungee plugins -->
<dependency>
<groupId>de.exlll</groupId>
<artifactId>configlib-bungee</artifactId>
<version>1.2.0</version>
<version>1.3.0-SNAPSHOT</version>
</dependency>
```
#### Gradle
```groovy
repositories {
maven {
url 'https://repo.exlll.de/artifactory/releases/'
url 'https://repo.exlll.de/artifactory/snapshots/'
}
}
dependencies {
// for Bukkit plugins
compile group: 'de.exlll', name: 'configlib-bukkit', version: '1.2.0'
compile group: 'de.exlll', name: 'configlib-bukkit', version: '1.3.0-SNAPSHOT'
// for Bungee plugins
compile group: 'de.exlll', name: 'configlib-bungee', version: '1.2.0'
compile group: 'de.exlll', name: 'configlib-bungee', version: '1.3.0-SNAPSHOT'
}
```
Additionally, you either have to import the Bukkit or BungeeCord API
@ -159,4 +159,4 @@ or disable transitive lookups. This project uses both of these APIs, so if you
need an example of how to import them with Gradle, take a look at the `build.gradle`.
If, for some reason, you have SSL errors that you're unable to resolve, you can
use `http://exlll.de:8081/artifactory/releases/` as the repository instead.
use `http://exlll.de:8081/artifactory/snapshots/` as the repository instead.

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

Loading…
Cancel
Save