diff --git a/configlib-core/src/main/java/de/exlll/configlib/SerializeWith.java b/configlib-core/src/main/java/de/exlll/configlib/SerializeWith.java
new file mode 100644
index 0000000..6b77108
--- /dev/null
+++ b/configlib-core/src/main/java/de/exlll/configlib/SerializeWith.java
@@ -0,0 +1,63 @@
+package de.exlll.configlib;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that the annotated element should be serialized with the given serializer. Serializers
+ * provided by this annotation take precedence over all other serializers.
+ *
+ * If the annotated element is an array, list, set, or map a nesting level can be set to apply the
+ * serializer not to the top-level type but to its elements. For maps, the serializer is applied to
+ * the values and not the keys.
+ *
+ * The following example shows how {@code nesting} can be used to apply the serializer at
+ * different levels.
+ *
+ *
+ * {@code
+ * // MyListSerializer is applied to 'list'
+ * @SerializeWith(serializer = MyListSerializer.class)
+ * List> list;
+ *
+ * // MySetSerializer is applied to the Set elements of 'list'
+ * @SerializeWith(serializer = MySetSerializer.class, nesting = 1)
+ * List> list;
+ *
+ * // MyStringSerializer is applied to the strings within the set elements of 'list'
+ * @SerializeWith(serializer = MyStringSerializer.class, nesting = 2)
+ * List> list;
+ *
+ * // MyMap0Serializer is applied to 'map'
+ * @SerializeWith(serializer = MyMap0Serializer.class)
+ * Map> map;
+ *
+ * // MyMap1Serializer is applied to the Map values of 'map'
+ * @SerializeWith(serializer = MyMap1Serializer.class, nesting = 1)
+ * Map> map;
+ *
+ * // MyDoubleSerializer is applied to the doubles within the nested values of 'map'
+ * @SerializeWith(serializer = MyDoubleSerializer.class, nesting = 2)
+ * Map> map;
+ * }
+ *
+ */
+@Target({ElementType.FIELD, ElementType.RECORD_COMPONENT})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SerializeWith {
+ /**
+ * Returns the type of the serializer to be used.
+ *
+ * @return the type of serializer to use
+ */
+ Class extends Serializer, ?>> serializer();
+
+ /**
+ * Returns the nesting level at which to apply the serializer.
+ *
+ * @return the nesting level
+ */
+ int nesting() default 0;
+}
diff --git a/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java b/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java
index 96e6e58..67bd7ba 100644
--- a/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java
+++ b/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java
@@ -50,12 +50,32 @@ final class SerializerSelector {
Map.entry(URI.class, new UriSerializer())
);
private final ConfigurationProperties properties;
+ /**
+ * Holds the {@code SerializeWith} value of the last {@literal select}ed component. If the
+ * component is not annotated with {@code SerializeWith}, the value of this field is null.
+ */
+ private SerializeWith serializeWith;
+ /**
+ * The {@code currentNesting} is used to determine the nesting of a type and is incremented each
+ * time the {@code selectForType} method is called. It is reset when {@code select} is called.
+ *
+ * For example, for a field {@code List>}, the nesting of {@code List} would be 0,
+ * the nesting of {@code Set} 1, and the nesting of {@code String} 2.
+ */
+ private int currentNesting = -1;
public SerializerSelector(ConfigurationProperties properties) {
this.properties = requireNonNull(properties, "configuration properties");
}
- public Serializer, ?> select(Type type) {
+ public Serializer, ?> select(TypeComponent> component) {
+ this.currentNesting = -1;
+ this.serializeWith = component.annotation(SerializeWith.class);
+ return selectForType(component.genericType());
+ }
+
+ private Serializer, ?> selectForType(Type type) {
+ this.currentNesting++;
final Serializer, ?> custom = selectCustomSerializer(type);
if (custom != null)
return custom;
@@ -78,6 +98,8 @@ final class SerializerSelector {
}
private Serializer, ?> selectCustomSerializer(Type type) {
+ if ((serializeWith != null) && (currentNesting == serializeWith.nesting()))
+ return Reflect.callNoParamConstructor(serializeWith.serializer());
if (type instanceof Class> cls) {
if (properties.getSerializers().containsKey(cls))
return properties.getSerializers().get(cls);
@@ -133,7 +155,7 @@ final class SerializerSelector {
} else if (elementType == double.class) {
return new PrimitiveDoubleArraySerializer();
}
- var elementSerializer = select(elementType);
+ var elementSerializer = selectForType(elementType);
var inputNulls = properties.inputNulls();
var outputNulls = properties.outputNulls();
return new ArraySerializer<>(elementType, elementSerializer, outputNulls, inputNulls);
@@ -147,10 +169,10 @@ final class SerializerSelector {
final var outputNulls = properties.outputNulls();
if (Reflect.isListType(rawType)) {
- var elementSerializer = select(typeArgs[0]);
+ var elementSerializer = selectForType(typeArgs[0]);
return new ListSerializer<>(elementSerializer, outputNulls, inputNulls);
} else if (Reflect.isSetType(rawType)) {
- var elementSerializer = select(typeArgs[0]);
+ var elementSerializer = selectForType(typeArgs[0]);
return properties.serializeSetsAsLists()
? new SetAsListSerializer<>(elementSerializer, outputNulls, inputNulls)
: new SetSerializer<>(elementSerializer, outputNulls, inputNulls);
@@ -158,8 +180,8 @@ final class SerializerSelector {
if ((typeArgs[0] instanceof Class> cls) &&
(DEFAULT_SERIALIZERS.containsKey(cls) ||
Reflect.isEnumType(cls))) {
- var keySerializer = select(typeArgs[0]);
- var valSerializer = select(typeArgs[1]);
+ var keySerializer = selectForClass(cls);
+ var valSerializer = selectForType(typeArgs[1]);
return new MapSerializer<>(keySerializer, valSerializer, outputNulls, inputNulls);
}
String msg = baseExceptionMessage(type) +
diff --git a/configlib-core/src/main/java/de/exlll/configlib/TypeComponent.java b/configlib-core/src/main/java/de/exlll/configlib/TypeComponent.java
index c3f5d7a..98f4178 100644
--- a/configlib-core/src/main/java/de/exlll/configlib/TypeComponent.java
+++ b/configlib-core/src/main/java/de/exlll/configlib/TypeComponent.java
@@ -14,7 +14,7 @@ import static de.exlll.configlib.Validator.requireNonNull;
*
* @param the type of the component
*/
-sealed interface TypeComponent {
+interface TypeComponent {
/**
* Returns the component itself.
*
diff --git a/configlib-core/src/main/java/de/exlll/configlib/TypeSerializer.java b/configlib-core/src/main/java/de/exlll/configlib/TypeSerializer.java
index 125fe68..19ef777 100644
--- a/configlib-core/src/main/java/de/exlll/configlib/TypeSerializer.java
+++ b/configlib-core/src/main/java/de/exlll/configlib/TypeSerializer.java
@@ -37,7 +37,7 @@ sealed abstract class TypeSerializer>
try {
return components().stream().collect(Collectors.toMap(
TypeComponent::name,
- component -> selector.select(component.genericType())
+ selector::select
));
} catch (StackOverflowError error) {
String msg = "Recursive type definitions are not supported.";
diff --git a/configlib-core/src/test/java/de/exlll/configlib/SerializerSelectorTest.java b/configlib-core/src/test/java/de/exlll/configlib/SerializerSelectorTest.java
index b126457..dd3fded 100644
--- a/configlib-core/src/test/java/de/exlll/configlib/SerializerSelectorTest.java
+++ b/configlib-core/src/test/java/de/exlll/configlib/SerializerSelectorTest.java
@@ -4,6 +4,7 @@ import de.exlll.configlib.Serializers.*;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.Mockito;
import java.awt.Point;
import java.io.File;
@@ -28,6 +29,7 @@ import static de.exlll.configlib.TestUtils.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
+@SuppressWarnings("unused")
class SerializerSelectorTest {
private static final SerializerSelector SELECTOR = new SerializerSelector(
ConfigurationProperties.newBuilder().build()
@@ -38,10 +40,21 @@ class SerializerSelectorTest {
return ls.getGenericType();
}
+ private static TypeComponent> comp(Type type) {
+ TypeComponent> mock = Mockito.mock(TypeComponent.class);
+ Mockito.when(mock.genericType()).thenReturn(type);
+ return mock;
+ }
+
+ private static TypeComponent> comp(Class> type, String fieldName) {
+ Field field = getField(type, fieldName);
+ return new TypeComponent.ConfigurationField(field);
+ }
+
@ParameterizedTest
@ValueSource(classes = {boolean.class, Boolean.class})
void selectSerializerBoolean(Class> cls) {
- Serializer, ?> serializer = SELECTOR.select(cls);
+ Serializer, ?> serializer = SELECTOR.select(comp(cls));
assertThat(serializer, instanceOf(BooleanSerializer.class));
}
@@ -52,99 +65,99 @@ class SerializerSelectorTest {
float.class, Float.class, double.class, Double.class
})
void selectSerializerNumber(Class> cls) {
- NumberSerializer serializer = (NumberSerializer) SELECTOR.select(cls);
+ NumberSerializer serializer = (NumberSerializer) SELECTOR.select(comp(cls));
assertThat(serializer.getNumberClass(), equalTo(cls));
}
@ParameterizedTest
@ValueSource(classes = {char.class, Character.class})
void selectSerializerChar(Class> cls) {
- Serializer, ?> serializer = SELECTOR.select(cls);
+ Serializer, ?> serializer = SELECTOR.select(comp(cls));
assertThat(serializer, instanceOf(CharacterSerializer.class));
}
@Test
void selectSerializerString() {
- Serializer, ?> serializer = SELECTOR.select(String.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(String.class));
assertThat(serializer, instanceOf(StringSerializer.class));
}
@Test
void selectSerializerBigInteger() {
- Serializer, ?> serializer = SELECTOR.select(BigInteger.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(BigInteger.class));
assertThat(serializer, instanceOf(BigIntegerSerializer.class));
}
@Test
void selectSerializerBigDecimal() {
- Serializer, ?> serializer = SELECTOR.select(BigDecimal.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(BigDecimal.class));
assertThat(serializer, instanceOf(BigDecimalSerializer.class));
}
@Test
void selectSerializerLocalDate() {
- Serializer, ?> serializer = SELECTOR.select(LocalDate.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(LocalDate.class));
assertThat(serializer, instanceOf(LocalDateSerializer.class));
}
@Test
void selectSerializerLocalTime() {
- Serializer, ?> serializer = SELECTOR.select(LocalTime.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(LocalTime.class));
assertThat(serializer, instanceOf(LocalTimeSerializer.class));
}
@Test
void selectSerializerLocalDateTime() {
- Serializer, ?> serializer = SELECTOR.select(LocalDateTime.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(LocalDateTime.class));
assertThat(serializer, instanceOf(LocalDateTimeSerializer.class));
}
@Test
void selectSerializerInstant() {
- Serializer, ?> serializer = SELECTOR.select(Instant.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(Instant.class));
assertThat(serializer, instanceOf(InstantSerializer.class));
}
@Test
void selectSerializerUuid() {
- Serializer, ?> serializer = SELECTOR.select(UUID.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(UUID.class));
assertThat(serializer, instanceOf(UuidSerializer.class));
}
@Test
void selectSerializerFile() {
- Serializer, ?> serializer = SELECTOR.select(File.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(File.class));
assertThat(serializer, instanceOf(FileSerializer.class));
}
@Test
void selectSerializerPath() {
- Serializer, ?> serializer = SELECTOR.select(Path.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(Path.class));
assertThat(serializer, instanceOf(PathSerializer.class));
}
@Test
void selectSerializerUrl() {
- Serializer, ?> serializer = SELECTOR.select(URL.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(URL.class));
assertThat(serializer, instanceOf(UrlSerializer.class));
}
@Test
void selectSerializerUri() {
- Serializer, ?> serializer = SELECTOR.select(URI.class);
+ Serializer, ?> serializer = SELECTOR.select(comp(URI.class));
assertThat(serializer, instanceOf(UriSerializer.class));
}
@Test
void selectSerializerEnum() {
enum E {}
- EnumSerializer serializer = (EnumSerializer) SELECTOR.select(E.class);
+ EnumSerializer serializer = (EnumSerializer) SELECTOR.select(comp(E.class));
assertThat(serializer.getEnumCls(), equalTo(E.class));
}
@Test
void selectSerializerArray() {
- var serializer = (ArraySerializer, ?>) SELECTOR.select(String[][].class);
+ var serializer = (ArraySerializer, ?>) SELECTOR.select(comp(String[][].class));
assertThat(serializer.getComponentType(), equalTo(String[].class));
var elementSerializer = (ArraySerializer, ?>) serializer.getElementSerializer();
@@ -154,49 +167,49 @@ class SerializerSelectorTest {
@Test
void selectSerializerPrimitiveBooleanArray() {
- Serializer, ?> serializer = SELECTOR.select(boolean[].class);
+ Serializer, ?> serializer = SELECTOR.select(comp(boolean[].class));
assertThat(serializer, instanceOf(PrimitiveBooleanArraySerializer.class));
}
@Test
void selectSerializerPrimitiveCharacterArray() {
- Serializer, ?> serializer = SELECTOR.select(char[].class);
+ Serializer, ?> serializer = SELECTOR.select(comp(char[].class));
assertThat(serializer, instanceOf(PrimitiveCharacterArraySerializer.class));
}
@Test
void selectSerializerPrimitiveByteArray() {
- Serializer, ?> serializer = SELECTOR.select(byte[].class);
+ Serializer, ?> serializer = SELECTOR.select(comp(byte[].class));
assertThat(serializer, instanceOf(PrimitiveByteArraySerializer.class));
}
@Test
void selectSerializerPrimitiveShortArray() {
- Serializer, ?> serializer = SELECTOR.select(short[].class);
+ Serializer, ?> serializer = SELECTOR.select(comp(short[].class));
assertThat(serializer, instanceOf(PrimitiveShortArraySerializer.class));
}
@Test
void selectSerializerPrimitiveIntegerArray() {
- Serializer, ?> serializer = SELECTOR.select(int[].class);
+ Serializer, ?> serializer = SELECTOR.select(comp(int[].class));
assertThat(serializer, instanceOf(PrimitiveIntegerArraySerializer.class));
}
@Test
void selectSerializerPrimitiveLongArray() {
- Serializer, ?> serializer = SELECTOR.select(long[].class);
+ Serializer, ?> serializer = SELECTOR.select(comp(long[].class));
assertThat(serializer, instanceOf(PrimitiveLongArraySerializer.class));
}
@Test
void selectSerializerPrimitiveFloatArray() {
- Serializer, ?> serializer = SELECTOR.select(float[].class);
+ Serializer, ?> serializer = SELECTOR.select(comp(float[].class));
assertThat(serializer, instanceOf(PrimitiveFloatArraySerializer.class));
}
@Test
void selectSerializerPrimitiveDoubleArray() {
- Serializer, ?> serializer = SELECTOR.select(double[].class);
+ Serializer, ?> serializer = SELECTOR.select(comp(double[].class));
assertThat(serializer, instanceOf(PrimitiveDoubleArraySerializer.class));
}
@@ -206,14 +219,14 @@ class SerializerSelectorTest {
class A {
int i;
}
- var serializer = (ConfigurationSerializer>) SELECTOR.select(A.class);
+ var serializer = (ConfigurationSerializer>) SELECTOR.select(comp(A.class));
assertThat(serializer.getConfigurationType(), equalTo(A.class));
}
@Test
void selectSerializerRecord() {
record R(int i) {}
- var serializer = (RecordSerializer>) SELECTOR.select(R.class);
+ var serializer = (RecordSerializer>) SELECTOR.select(comp(R.class));
assertThat(serializer.getRecordType(), equalTo(R.class));
}
@@ -221,14 +234,14 @@ class SerializerSelectorTest {
void recordSerializerTakesPrecedenceOverConfigurationSerializer() {
@Configuration
record R(int i) {}
- var serializer = (RecordSerializer>) SELECTOR.select(R.class);
+ var serializer = (RecordSerializer>) SELECTOR.select(comp(R.class));
assertThat(serializer.getRecordType(), equalTo(R.class));
}
@Test
void selectSerializerMissingType() {
assertThrowsConfigurationException(
- () -> SELECTOR.select(Object.class),
+ () -> SELECTOR.select(comp(Object.class)),
"Missing serializer for type class java.lang.Object.\nEither annotate the type with " +
"@Configuration or provide a custom serializer by adding it to the properties."
);
@@ -240,7 +253,7 @@ class SerializerSelectorTest {
.addSerializer(Point.class, POINT_SERIALIZER)
.build();
SerializerSelector selector = new SerializerSelector(properties);
- var pointSerializer = selector.select(Point.class);
+ var pointSerializer = selector.select(comp(Point.class));
assertThat(pointSerializer, sameInstance(POINT_SERIALIZER));
}
@@ -250,7 +263,7 @@ class SerializerSelectorTest {
.addSerializer(BigInteger.class, CUSTOM_BIG_INTEGER_SERIALIZER)
.build();
SerializerSelector selector = new SerializerSelector(properties);
- var bigIntegerSerializer = selector.select(BigInteger.class);
+ var bigIntegerSerializer = selector.select(comp(BigInteger.class));
assertThat(bigIntegerSerializer, instanceOf(TestUtils.CustomBigIntegerSerializer.class));
assertThat(bigIntegerSerializer, sameInstance(CUSTOM_BIG_INTEGER_SERIALIZER));
}
@@ -261,7 +274,7 @@ class SerializerSelectorTest {
.addSerializerByCondition(t -> t == Point.class, POINT_SERIALIZER)
.build();
SerializerSelector selector = new SerializerSelector(properties);
- var pointSerializer = selector.select(Point.class);
+ var pointSerializer = selector.select(comp(Point.class));
assertThat(pointSerializer, sameInstance(POINT_SERIALIZER));
}
@@ -271,7 +284,7 @@ class SerializerSelectorTest {
.addSerializerByCondition(t -> t == BigInteger.class, CUSTOM_BIG_INTEGER_SERIALIZER)
.build();
SerializerSelector selector = new SerializerSelector(properties);
- var bigIntegerSerializer = selector.select(BigInteger.class);
+ var bigIntegerSerializer = selector.select(comp(BigInteger.class));
assertThat(bigIntegerSerializer, instanceOf(TestUtils.CustomBigIntegerSerializer.class));
assertThat(bigIntegerSerializer, sameInstance(CUSTOM_BIG_INTEGER_SERIALIZER));
}
@@ -285,7 +298,7 @@ class SerializerSelectorTest {
.addSerializer(int.class, serializer2)
.build();
SerializerSelector selector = new SerializerSelector(properties);
- var serializer = selector.select(int.class);
+ var serializer = selector.select(comp(int.class));
assertThat(serializer, instanceOf(IdentifiableSerializer.class));
assertThat(serializer, sameInstance(serializer2));
}
@@ -295,7 +308,7 @@ class SerializerSelectorTest {
class A {
List ls;
}
- var serializer = (ListSerializer, ?>) SELECTOR.select(getGenericType(A.class, "ls"));
+ var serializer = (ListSerializer, ?>) SELECTOR.select(comp(getGenericType(A.class, "ls")));
assertThat(serializer.getElementSerializer(), instanceOf(StringSerializer.class));
}
@@ -304,7 +317,7 @@ class SerializerSelectorTest {
class A {
List> lls;
}
- var serializer = (ListSerializer, ?>) SELECTOR.select(getGenericType(A.class, "lls"));
+ var serializer = (ListSerializer, ?>) SELECTOR.select(comp(getGenericType(A.class, "lls")));
var elementSerializer = (ListSerializer, ?>) serializer.getElementSerializer();
assertThat(elementSerializer.getElementSerializer(), instanceOf(StringSerializer.class));
}
@@ -317,7 +330,7 @@ class SerializerSelectorTest {
SerializerSelector selector = new SerializerSelector(
ConfigurationProperties.newBuilder().serializeSetsAsLists(false).build()
);
- var serializer = (SetSerializer, ?>) selector.select(getGenericType(A.class, "ss"));
+ var serializer = (SetSerializer, ?>) selector.select(comp(getGenericType(A.class, "ss")));
assertThat(serializer.getElementSerializer(), instanceOf(StringSerializer.class));
}
@@ -326,7 +339,7 @@ class SerializerSelectorTest {
class A {
Set ss;
}
- var serializer = (SetAsListSerializer, ?>) SELECTOR.select(getGenericType(A.class, "ss"));
+ var serializer = (SetAsListSerializer, ?>) SELECTOR.select(comp(getGenericType(A.class, "ss")));
assertThat(serializer.getElementSerializer(), instanceOf(StringSerializer.class));
}
@@ -335,7 +348,7 @@ class SerializerSelectorTest {
class A {
Map mis;
}
- var serializer = (MapSerializer, ?, ?, ?>) SELECTOR.select(getGenericType(A.class, "mis"));
+ var serializer = (MapSerializer, ?, ?, ?>) SELECTOR.select(comp(getGenericType(A.class, "mis")));
var numberSerializer = (NumberSerializer) serializer.getKeySerializer();
assertThat(numberSerializer.getNumberClass(), equalTo(Integer.class));
assertThat(serializer.getValueSerializer(), instanceOf(StringSerializer.class));
@@ -347,7 +360,7 @@ class SerializerSelectorTest {
class A {
Map>> mesle;
}
- var serializer = (MapSerializer, ?, ?, ?>) SELECTOR.select(getGenericType(A.class, "mesle"));
+ var serializer = (MapSerializer, ?, ?, ?>) SELECTOR.select(comp(getGenericType(A.class, "mesle")));
var keySerializer = (EnumSerializer) serializer.getKeySerializer();
assertThat(keySerializer.getEnumCls(), equalTo(E.class));
@@ -365,7 +378,7 @@ class SerializerSelectorTest {
}
Type type = getGenericType(A.class, "mlss");
assertThrowsConfigurationException(
- () -> SELECTOR.select(type),
+ () -> SELECTOR.select(comp(type)),
"Cannot select serializer for type '" + type + "'.\nMap keys can only be " +
"of simple or enum type."
);
@@ -378,7 +391,7 @@ class SerializerSelectorTest {
}
Type type = getGenericType(A.class, "mps");
assertThrowsConfigurationException(
- () -> SELECTOR.select(type),
+ () -> SELECTOR.select(comp(type)),
"Cannot select serializer for type '" + type + "'.\nMap keys can only be " +
"of simple or enum type."
);
@@ -392,7 +405,7 @@ class SerializerSelectorTest {
}
Type type = getGenericType(A.class, "box");
assertThrowsConfigurationException(
- () -> SELECTOR.select(type),
+ () -> SELECTOR.select(comp(type)),
"Cannot select serializer for type '" + type + "'.\nParameterized " +
"types other than lists, sets, and maps cannot be serialized."
);
@@ -405,7 +418,7 @@ class SerializerSelectorTest {
}
Type type = getGenericType(A.class, "ga");
assertThrowsConfigurationException(
- () -> SELECTOR.select(type),
+ () -> SELECTOR.select(comp(type)),
"Cannot select serializer for type 'java.util.List>[]'.\n" +
"Generic array types cannot be serialized."
);
@@ -419,7 +432,7 @@ class SerializerSelectorTest {
ParameterizedType ptype = (ParameterizedType) getGenericType(A.class, "les");
Type type = ptype.getActualTypeArguments()[0];
assertThrowsConfigurationException(
- () -> SELECTOR.select(type),
+ () -> SELECTOR.select(comp(type)),
"Cannot select serializer for type '? extends java.lang.String'.\n" +
"Wildcard types cannot be serialized."
);
@@ -433,7 +446,7 @@ class SerializerSelectorTest {
ParameterizedType ptype = (ParameterizedType) getGenericType(A.class, "lw");
Type type = ptype.getActualTypeArguments()[0];
assertThrowsConfigurationException(
- () -> SELECTOR.select(type),
+ () -> SELECTOR.select(comp(type)),
"Cannot select serializer for type '?'.\n" +
"Wildcard types cannot be serialized."
);
@@ -446,9 +459,143 @@ class SerializerSelectorTest {
}
Type type = getGenericType(A.class, "t");
assertThrowsConfigurationException(
- () -> SELECTOR.select(type),
+ () -> SELECTOR.select(comp(type)),
"Cannot select serializer for type 'T'.\n" +
"Type variables cannot be serialized."
);
}
-}
\ No newline at end of file
+
+ static final class SerializeWithTests {
+ static class Z {
+ @SerializeWith(serializer = IdentitySerializer.class)
+ String string;
+ @SerializeWith(serializer = IdentitySerializer.class)
+ List> list1;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 1)
+ List> list2;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 2)
+ List> list3;
+ @SerializeWith(serializer = IdentitySerializer.class)
+ Set> set1;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 1)
+ Set> set2;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 2)
+ Set> set3;
+ @SerializeWith(serializer = IdentitySerializer.class)
+ Map> map1;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 1)
+ Map> map2;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 2)
+ Map> map3;
+ @SerializeWith(serializer = IdentitySerializer.class)
+ String[][] array1;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 1)
+ String[][] array2;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 2)
+ String[][] array3;
+ }
+
+ @Test
+ void selectCustomSerializerForField() {
+ var serializer = SELECTOR.select(comp(Z.class, "string"));
+ assertThat(serializer, instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForListsWithNesting0() {
+ var serializer = SELECTOR.select(comp(Z.class, "list1"));
+ assertThat(serializer, instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForListsWithNesting1() {
+ var serializer = (ListSerializer, ?>) SELECTOR.select(comp(Z.class, "list2"));
+ assertThat(serializer.getElementSerializer(), instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForListsWithNesting2() {
+ var serializer1 = (ListSerializer, ?>) SELECTOR.select(comp(Z.class, "list3"));
+ var serializer2 = (SetAsListSerializer, ?>) serializer1.getElementSerializer();
+ assertThat(serializer2.getElementSerializer(), instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForSetsWithNesting0() {
+ var serializer = SELECTOR.select(comp(Z.class, "set1"));
+ assertThat(serializer, instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForSetsWithNesting1() {
+ var serializer = (SetAsListSerializer, ?>) SELECTOR.select(comp(Z.class, "set2"));
+ assertThat(serializer.getElementSerializer(), instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForSetsWithNesting2() {
+ var serializer1 = (SetAsListSerializer, ?>) SELECTOR.select(comp(Z.class, "set3"));
+ var serializer2 = (ListSerializer, ?>) serializer1.getElementSerializer();
+ assertThat(serializer2.getElementSerializer(), instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForMapsWithNesting0() {
+ var serializer = SELECTOR.select(comp(Z.class, "map1"));
+ assertThat(serializer, instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForMapsWithNesting1() {
+ var serializer = (MapSerializer, ?, ?, ?>) SELECTOR.select(comp(Z.class, "map2"));
+ assertThat(serializer.getKeySerializer(), instanceOf(NumberSerializer.class));
+ assertThat(serializer.getValueSerializer(), instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForMapsWithNesting2() {
+ var serializer1 = (MapSerializer, ?, ?, ?>) SELECTOR.select(comp(Z.class, "map3"));
+ var serializer2 = (MapSerializer, ?, ?, ?>) serializer1.getValueSerializer();
+ assertThat(serializer2.getKeySerializer(), instanceOf(StringSerializer.class));
+ assertThat(serializer2.getValueSerializer(), instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForArraysWithNesting0() {
+ var serializer = SELECTOR.select(comp(Z.class, "array1"));
+ assertThat(serializer, instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForArraysWithNesting1() {
+ var serializer = (ArraySerializer, ?>) SELECTOR.select(comp(Z.class, "array2"));
+ assertThat(serializer.getElementSerializer(), instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerForArraysWithNesting2() {
+ var serializer1 = (ArraySerializer, ?>) SELECTOR.select(comp(Z.class, "array3"));
+ var serializer2 = (ArraySerializer, ?>) serializer1.getElementSerializer();
+ assertThat(serializer2.getElementSerializer(), instanceOf(IdentitySerializer.class));
+ }
+
+ @Test
+ void selectCustomSerializerWithInvalidNestingNotSelected() {
+ class A {
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = -1)
+ String s1;
+ @SerializeWith(serializer = IdentitySerializer.class)
+ String s2;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 1)
+ String s3;
+ @SerializeWith(serializer = IdentitySerializer.class, nesting = 2)
+ List list;
+ }
+ assertThat(SELECTOR.select(comp(A.class, "s1")), instanceOf(StringSerializer.class));
+ assertThat(SELECTOR.select(comp(A.class, "s2")), instanceOf(IdentitySerializer.class));
+ assertThat(SELECTOR.select(comp(A.class, "s3")), instanceOf(StringSerializer.class));
+ var serializer = (ListSerializer, ?>) SELECTOR.select(comp(A.class, "list"));
+ assertThat(serializer.getElementSerializer(), instanceOf(StringSerializer.class));
+ }
+ }
+}
diff --git a/configlib-core/src/testFixtures/java/de/exlll/configlib/TestUtils.java b/configlib-core/src/testFixtures/java/de/exlll/configlib/TestUtils.java
index abad0c2..2d825f1 100644
--- a/configlib-core/src/testFixtures/java/de/exlll/configlib/TestUtils.java
+++ b/configlib-core/src/testFixtures/java/de/exlll/configlib/TestUtils.java
@@ -127,6 +127,19 @@ public final class TestUtils {
}
}
+ public static final class IdentitySerializer implements Serializer