diff --git a/core/pom.xml b/core/pom.xml
index 4a285d5..5f9a61e 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -18,14 +18,6 @@
easyconfiguration-core
jar
-
-
- cc.carm.lib
- easyoptions
- 1.1.0
-
-
-
diff --git a/core/src/main/java/cc/carm/lib/configuration/Configuration.java b/core/src/main/java/cc/carm/lib/configuration/Configuration.java
index 3596cf7..26a52a1 100644
--- a/core/src/main/java/cc/carm/lib/configuration/Configuration.java
+++ b/core/src/main/java/cc/carm/lib/configuration/Configuration.java
@@ -2,8 +2,6 @@ package cc.carm.lib.configuration;
/**
* The root interface of the configuration file interfaces,
- * which is used to label and record the configuration information.
+ * which is used to label a class as a configuration.
*/
-public interface Configuration {
-
-}
+public interface Configuration { }
\ No newline at end of file
diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapter.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapter.java
index deeb81d..6fe33a3 100644
--- a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapter.java
+++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapter.java
@@ -1,52 +1,77 @@
package cc.carm.lib.configuration.adapter;
import cc.carm.lib.configuration.source.ConfigurationProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Objects;
/**
* Value adapter, used to convert the value of the configuration file into the objects.
*
- * @param The type of the base data
- * @param The type of the target value
+ * @param The type of the target value
*/
-public abstract class ValueAdapter implements ValueSerializer, ValueDeserializer {
+public class ValueAdapter
+ implements ValueSerializer, ValueParser {
- protected final Class super B> baseType;
- protected final Class super V> valueType;
+ protected final @NotNull ValueType type;
+ protected @Nullable ValueSerializer serializer;
+ protected @Nullable ValueParser deserializer;
- protected ValueAdapter(Class super B> baseType, Class super V> valueType) {
- this.baseType = baseType;
- this.valueType = valueType;
+ public ValueAdapter(@NotNull ValueType type) {
+ this(type, null, null);
}
- public Class super B> getBaseClass() {
- return baseType;
+ public ValueAdapter(@NotNull ValueType type,
+ @Nullable ValueSerializer serializer,
+ @Nullable ValueParser deserializer) {
+ this.type = type;
+ this.serializer = serializer;
+ this.deserializer = deserializer;
}
- public Class super V> getValueClass() {
- return valueType;
+ public @NotNull ValueType type() {
+ return type;
}
- public boolean isAdaptedFrom(Class> clazz) {
- return clazz.isAssignableFrom(valueType);
+ public @Nullable ValueSerializer serializer() {
+ return serializer;
}
- public boolean isAdaptedFrom(Object object) {
- return isAdaptedFrom(object.getClass());
+ public @Nullable ValueParser deserializer() {
+ return deserializer;
}
- public boolean isAdaptedTo(Class> clazz) {
- return clazz == valueType;
+ public void serializer(@Nullable ValueSerializer serializer) {
+ this.serializer = serializer;
}
- @SuppressWarnings("unchecked")
- protected final V deserializeObject(ConfigurationProvider> provider, Class> valueClass, Object data) throws Exception {
- return deserialize(provider, (Class extends V>) valueClass, (B) data);
+ public void deserializer(@Nullable ValueParser deserializer) {
+ this.deserializer = deserializer;
}
- @SuppressWarnings("unchecked")
- protected final B serializeObject(ConfigurationProvider> provider, Object value) throws Exception {
- return serialize(provider, (V) value);
+ @Override
+ public Object serialize(@NotNull ConfigurationProvider> provider, @NotNull ValueType super TYPE> type, @NotNull TYPE value) throws Exception {
+ if (serializer == null) throw new UnsupportedOperationException("Serializer is not supported");
+ return serializer.serialize(provider, type, value);
}
+ @Override
+ public TYPE deserialize(@NotNull ConfigurationProvider> provider, @NotNull ValueType super TYPE> type, @NotNull Object value) throws Exception {
+ if (deserializer == null) throw new UnsupportedOperationException("Deserializer is not supported");
+ return deserializer.deserialize(provider, type, value);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof ValueAdapter)) return false;
+ ValueAdapter> that = (ValueAdapter>) o;
+ return Objects.equals(type, that.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(type);
+ }
}
diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapterRegistry.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapterRegistry.java
index 3216c79..3bb7e67 100644
--- a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapterRegistry.java
+++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapterRegistry.java
@@ -1,91 +1,125 @@
package cc.carm.lib.configuration.adapter;
-import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapters;
import cc.carm.lib.configuration.function.ConfigDataFunction;
import cc.carm.lib.configuration.source.ConfigurationProvider;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
public class ValueAdapterRegistry {
- protected final Map, ValueAdapter, ?>> adapters = new HashMap<>();
+ protected final Set> adapters = new HashSet<>();
- public void register(@NotNull ValueAdapter, ?> adapter) {
- adapters.put(adapter.getValueClass(), adapter);
+ public void register(@NotNull Class from, @NotNull Class to,
+ @Nullable ConfigDataFunction parser,
+ @Nullable ConfigDataFunction serializer) {
+ register(ValueType.of(from), ValueType.of(to), parser, serializer);
}
- public void register(Class clazz, @NotNull ValueAdapter, T> adapter) {
- adapters.put(clazz, adapter);
+ public void register(@NotNull ValueType from, @NotNull ValueType to,
+ @Nullable ConfigDataFunction parser,
+ @Nullable ConfigDataFunction serializer) {
+ ValueAdapter fromAdapter = adapterOf(from);
+ if (fromAdapter == null) throw new IllegalArgumentException("No adapter for type " + from);
+ register(to,
+ serializer == null ? null : (provider, type, value) -> fromAdapter.serialize(provider, from, serializer.handle(value)),
+ parser == null ? null : (provider, type, data) -> parser.handle(fromAdapter.deserialize(provider, from, data))
+ );
}
- public void register(Class baseClass, Class valueClass,
- ConfigDataFunction parser,
- ConfigDataFunction serializer) {
- register(new ValueAdapter(baseClass, valueClass) {
- @Override
- public B serialize(@NotNull ConfigurationProvider> provider, @NotNull V value) throws Exception {
- return serializer.parse(value);
- }
-
- @Override
- public V deserialize(@NotNull ConfigurationProvider> provider, @NotNull Class extends V> clazz, @NotNull B data) throws Exception {
- return parser.parse(data);
- }
- });
+ public void register(@NotNull ValueAdapter>... adapter) {
+ adapters.addAll(Arrays.asList(adapter));
}
- public void unregister(@NotNull Class> typeClass) {
- adapters.remove(typeClass);
+ public void register(@NotNull Class type, @NotNull ValueSerializer serializer) {
+ register(ValueType.of(type), serializer);
+ }
+
+ public void register(@NotNull ValueType type, @NotNull ValueSerializer serializer) {
+ ValueAdapter existing = adapterOf(type);
+ if (existing != null) {
+ existing.serializer(serializer);
+ } else {
+ register(new ValueAdapter<>(type, serializer, null));
+ }
+ }
+
+ public void register(@NotNull Class type, @NotNull ValueParser deserializer) {
+ register(ValueType.of(type), deserializer);
+ }
+
+ public void register(@NotNull ValueType type, @NotNull ValueParser deserializer) {
+ ValueAdapter existing = adapterOf(type);
+ if (existing != null) {
+ existing.deserializer(deserializer);
+ } else {
+ register(new ValueAdapter<>(type, null, deserializer));
+ }
+ }
+
+ public void register(@NotNull ValueType type, @Nullable ValueSerializer serializer, @Nullable ValueParser deserializer) {
+ if (serializer == null && deserializer == null) return;
+ ValueAdapter existing = adapterOf(type);
+ if (existing != null) {
+ if (serializer != null) existing.serializer(serializer);
+ if (deserializer != null) existing.deserializer(deserializer);
+ } else {
+ register(new ValueAdapter<>(type, serializer, deserializer));
+ }
+ }
+
+ public void unregister(@NotNull Class> type) {
+ unregister(ValueType.of(type));
+ }
+
+ public void unregister(@NotNull ValueType> type) {
+ adapters.removeIf(adapter -> adapter.type().equals(type));
}
@SuppressWarnings("unchecked")
+ public ValueAdapter adapterOf(@NotNull ValueType type) {
+ ValueAdapter> matched = adapters.stream().filter(adapter -> adapter.type().equals(type)).findFirst().orElse(null);
+ if (matched != null) return (ValueAdapter) matched;
+
+ // If no adapter found, try to find the adapter for the super type
+ return (ValueAdapter) adapters.stream()
+ .filter(adapter -> adapter.type().isSubtypeOf(type))
+ .findFirst().orElse(null);
+ }
+
+ public ValueAdapter adapterOf(@NotNull T value) {
+ return adapterOf(ValueType.of(value));
+ }
+
+ public ValueAdapter adapterOf(@NotNull Class type) {
+ return adapterOf(ValueType.of(type));
+ }
+
@Contract("_,_,null -> null")
public T deserialize(@NotNull ConfigurationProvider> provider, @NotNull Class type, @Nullable Object source) throws Exception {
+ return deserialize(provider, ValueType.of(type), source);
+ }
+
+ @Contract("_,_,null -> null")
+ public T deserialize(@NotNull ConfigurationProvider> provider, @NotNull ValueType type, @Nullable Object source) throws Exception {
if (source == null) return null; // Null check
if (type.isInstance(source)) return type.cast(source); // Not required to deserialize
-
- ValueAdapter, ?> adapter = getAdapter(type);
- if (adapter == null) throw new RuntimeException("No adapter for type " + type.getName());
-
- // Check if value is adapted from given value's type
- if (adapter.isAdaptedFrom(source)) {
- return (T) adapter.deserializeObject(provider, type, source);
- }
-
- // Otherwise, we need to deserialize one by one.
- Object baseValue = deserialize(provider, adapter.getBaseClass(), source);
- if (baseValue == null) return null; // Null check
-
- return (T) adapter.deserializeObject(provider, type, baseValue);
+ ValueAdapter adapter = adapterOf(type);
+ if (adapter == null) throw new RuntimeException("No adapter for type " + type);
+ return adapter.deserialize(provider, type, source);
}
@Contract("_,null -> null")
public Object serialize(@NotNull ConfigurationProvider> provider, @Nullable T value) throws Exception {
if (value == null) return null; // Null check
-
- ValueAdapter, ?> adapter = getAdapter(value.getClass());
+ ValueType type = ValueType.of(value);
+ ValueAdapter adapter = adapterOf(type);
if (adapter == null) return value; // No adapters, try to return the original value
-
- if (adapter instanceof PrimitiveAdapters) {
- // If the value is adapted from a primitive type,
- // we should serialize it into object, then return.
- return adapter.serializeObject(provider, value);
- }
-
- // Otherwise, we need to serialize one by one.
- return serialize(provider, adapter.serializeObject(provider, value));
- }
-
- public ValueAdapter, ?> getAdapter(Class> clazz) {
- return adapters.getOrDefault(clazz, findAdapter(clazz));
- }
-
- public ValueAdapter, ?> findAdapter(Class> clazz) {
- return adapters.values().stream().filter(adapter -> adapter.isAdaptedTo(clazz)).findFirst().orElse(null);
+ return adapter.serialize(provider, type, value);
}
diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueDeserializer.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueDeserializer.java
deleted file mode 100644
index 2f63ebb..0000000
--- a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueDeserializer.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package cc.carm.lib.configuration.adapter;
-
-import cc.carm.lib.configuration.source.ConfigurationProvider;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Value deserializer, convert base data to target value.
- *
- * @param The type of base data
- * @param The type of target value
- */
-@FunctionalInterface
-public interface ValueDeserializer {
-
- V deserialize(@NotNull ConfigurationProvider> provider, @NotNull Class extends V> clazz, @NotNull B data) throws Exception;
-
-}
diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueParser.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueParser.java
new file mode 100644
index 0000000..b35b089
--- /dev/null
+++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueParser.java
@@ -0,0 +1,19 @@
+package cc.carm.lib.configuration.adapter;
+
+import cc.carm.lib.configuration.source.ConfigurationProvider;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Value deserializer, convert base data to target value.
+ *
+ * @param The type of target value
+ */
+@FunctionalInterface
+public interface ValueParser {
+
+ TYPE deserialize(
+ @NotNull ConfigurationProvider> provider,
+ @NotNull ValueType super TYPE> type, @NotNull Object data
+ ) throws Exception;
+
+}
diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueSerializer.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueSerializer.java
index d27e8c9..44db8a9 100644
--- a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueSerializer.java
+++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueSerializer.java
@@ -6,12 +6,14 @@ import org.jetbrains.annotations.NotNull;
/**
* Value serializer, convert target value to base data.
*
- * @param The type of base data
- * @param The type of value
+ * @param The type of value
*/
@FunctionalInterface
-public interface ValueSerializer {
+public interface ValueSerializer {
- B serialize(@NotNull ConfigurationProvider> provider, @NotNull V value) throws Exception;
+ Object serialize(
+ @NotNull ConfigurationProvider> provider,
+ @NotNull ValueType super TYPE> type, @NotNull TYPE value
+ ) throws Exception;
}
diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueType.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueType.java
new file mode 100644
index 0000000..ca2edaf
--- /dev/null
+++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueType.java
@@ -0,0 +1,150 @@
+package cc.carm.lib.configuration.adapter;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.reflect.ParameterizedType;
+
+import java.lang.reflect.Type;
+import java.util.Objects;
+
+/**
+ * Used to get the generic type.
+ */
+public abstract class ValueType {
+
+ @SuppressWarnings("unchecked")
+ public static ValueType of(@NotNull T value) {
+ return of((Class) value.getClass());
+ }
+
+ public static ValueType of(final Type type) {
+ return new ValueType(type) {
+ };
+ }
+
+ public static ValueType of(final Class clazz) {
+ return of((Type) clazz);
+ }
+
+ /**
+ * Get the generic type of the complex type.
+ *
+ * @param rawType The raw type
+ * @param types The type arguments
+ * @param The type
+ * @return The {@link ValueType}
+ */
+ public static ValueType of(final Class> rawType, final Type... types) {
+ ParameterizedType parameterizedType = new ParameterizedType() {
+ @Override
+ public @NotNull Type @NotNull [] getActualTypeArguments() {
+ return types;
+ }
+
+ @Override
+ public @NotNull Type getRawType() {
+ return rawType;
+ }
+
+ @Override
+ public Type getOwnerType() {
+ return null;
+ }
+ };
+ return of(parameterizedType);
+ }
+
+ private final Type type;
+
+ protected ValueType() {
+ this.type = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
+ }
+
+ private ValueType(Type type) {
+ this.type = type;
+ }
+
+ public Type getType() {
+ return type;
+ }
+
+ public boolean isSubtypeOf(Class> target) {
+ Class> rawType = getRawType();
+ return target.isAssignableFrom(rawType);
+ }
+
+ public boolean isSubtypeOf(ValueType> target) {
+ return target.isSubtypeOf(getRawType());
+ }
+
+ public boolean isInstance(Object obj) {
+ return obj != null && getRawType().isInstance(obj);
+ }
+
+
+ /**
+ * 提取当前 ValueType 的原始类型(Class 对象)。
+ *
+ * @return 对应的 Class 对象
+ * @throws IllegalStateException 如果无法提取出原始类型
+ */
+ public Class> getRawType() {
+ if (type instanceof Class>) {
+ return (Class>) type;
+ }
+ if (type instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) type;
+ Type raw = pt.getRawType();
+ if (raw instanceof Class>) {
+ return (Class>) raw;
+ }
+ }
+ throw new IllegalStateException("Unsupported type: " + type);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T cast(Object obj) {
+ if (!isInstance(obj)) {
+ throw new ClassCastException("Cannot cast object " + obj + " to type " + this);
+ }
+ return (T) obj;
+ }
+
+ @Override
+ public String toString() {
+ if (type instanceof Class>) {
+ return ((Class>) type).getName();
+ }
+ if (type instanceof ParameterizedType) {
+ ParameterizedType pt = (ParameterizedType) type;
+ Type raw = pt.getRawType();
+ StringBuilder sb = new StringBuilder();
+ sb.append(raw.getTypeName());
+ sb.append('<');
+ Type[] args = pt.getActualTypeArguments();
+ for (int i = 0; i < args.length; i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(args[i].getTypeName());
+ }
+ return sb.toString();
+ }
+ return type.getTypeName();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj instanceof ValueType) {
+ return Objects.equals(type, ((ValueType>) obj).type);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(type);
+ }
+
+}
diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/EnumAdapter.java b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/EnumAdapter.java
deleted file mode 100644
index ee4a3a4..0000000
--- a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/EnumAdapter.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package cc.carm.lib.configuration.adapter.strandard;
-
-import cc.carm.lib.configuration.adapter.ValueAdapter;
-import cc.carm.lib.configuration.source.ConfigurationProvider;
-import org.jetbrains.annotations.NotNull;
-
-@SuppressWarnings({"unchecked", "rawtypes"})
-public class EnumAdapter extends ValueAdapter {
-
- public EnumAdapter() {
- super(String.class, Enum.class);
- }
-
- @Override
- public String serialize(@NotNull ConfigurationProvider> provider, @NotNull Enum value) throws Exception {
- return value.name();
- }
-
- @Override
- public Enum deserialize(@NotNull ConfigurationProvider> provider, @NotNull Class extends Enum> clazz, @NotNull String data) throws Exception {
- return Enum.valueOf(clazz, data);
- }
-
- @Override
- public boolean isAdaptedTo(Class> clazz) {
- return clazz.isEnum();
- }
-
-}
diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapters.java b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapters.java
index 5a36a73..f522bed 100644
--- a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapters.java
+++ b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapters.java
@@ -1,11 +1,27 @@
package cc.carm.lib.configuration.adapter.strandard;
import cc.carm.lib.configuration.adapter.ValueAdapter;
+import cc.carm.lib.configuration.adapter.ValueParser;
+import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.function.ConfigDataFunction;
-import cc.carm.lib.configuration.source.ConfigurationProvider;
import org.jetbrains.annotations.NotNull;
-public abstract class PrimitiveAdapters extends ValueAdapter