diff --git a/core/src/main/java/cc/carm/lib/configuration/Configuration.java b/core/src/main/java/cc/carm/lib/configuration/Configuration.java new file mode 100644 index 0000000..26a52a1 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/Configuration.java @@ -0,0 +1,7 @@ +package cc.carm.lib.configuration; + +/** + * The root interface of the configuration file interfaces, + * which is used to label a class as a 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 new file mode 100644 index 0000000..9df8f70 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapter.java @@ -0,0 +1,79 @@ +package cc.carm.lib.configuration.adapter; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +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 target value + */ +public class ValueAdapter + implements ValueSerializer, ValueParser { + + protected final @NotNull ValueType type; + protected @Nullable ValueSerializer serializer; + protected @Nullable ValueParser deserializer; + + public ValueAdapter(@NotNull ValueType type) { + this(type, null, null); + } + + public ValueAdapter(@NotNull ValueType type, + @Nullable ValueSerializer serializer, + @Nullable ValueParser parser) { + this.type = type; + this.serializer = serializer; + this.deserializer = parser; + } + + public @NotNull ValueType type() { + return type; + } + + public @Nullable ValueSerializer serializer() { + return serializer; + } + + public @Nullable ValueParser parser() { + return deserializer; + } + + public ValueAdapter serializer(@Nullable ValueSerializer serializer) { + this.serializer = serializer; + return this; + } + + public ValueAdapter parser(@Nullable ValueParser deserializer) { + this.deserializer = deserializer; + return this; + } + + @Override + public Object serialize(@NotNull ConfigurationHolder holder, @NotNull ValueType type, @NotNull TYPE value) throws Exception { + if (serializer == null) throw new UnsupportedOperationException("Serializer is not supported"); + return serializer.serialize(holder, type, value); + } + + @Override + public TYPE parse(@NotNull ConfigurationHolder holder, @NotNull ValueType type, @NotNull Object value) throws Exception { + if (deserializer == null) throw new UnsupportedOperationException("Deserializer is not supported"); + return deserializer.parse(holder, 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 new file mode 100644 index 0000000..f15d6f2 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapterRegistry.java @@ -0,0 +1,126 @@ +package cc.carm.lib.configuration.adapter; + +import cc.carm.lib.configuration.function.DataFunction; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class ValueAdapterRegistry { + + protected final Set> adapters = new HashSet<>(); + + public void register(@NotNull Class from, @NotNull Class to, + @Nullable DataFunction parser, + @Nullable DataFunction serializer) { + register(ValueType.of(from), ValueType.of(to), parser, serializer); + } + + public void register(@NotNull ValueType from, @NotNull ValueType to, + @Nullable DataFunction parser, + @Nullable DataFunction 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.parse(provider, from, data)) + ); + } + + public void register(@NotNull ValueAdapter... adapter) { + adapters.addAll(Arrays.asList(adapter)); + } + + 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.parser(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.parser(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 @Nullable 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 ConfigurationHolder holder, @NotNull Class type, @Nullable Object source) throws Exception { + return deserialize(holder, ValueType.of(type), source); + } + + @Contract("_,_,null -> null") + public T deserialize(@NotNull ConfigurationHolder holder, @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 = adapterOf(type); + if (adapter == null) throw new RuntimeException("No adapter for type " + type); + return adapter.parse(holder, type, source); + } + + @Contract("_,null -> null") + public Object serialize(@NotNull ConfigurationHolder holder, @Nullable T value) throws Exception { + if (value == null) return null; // Null check + ValueType type = ValueType.of(value); + ValueAdapter adapter = adapterOf(type); + if (adapter == null) return value; // No adapters, try to return the original value + return adapter.serialize(holder, type, value); + } + + +} 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..e5162b4 --- /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.ConfigurationHolder; +import org.jetbrains.annotations.NotNull; + +/** + * Value deserializer, convert base data to target value. + * + * @param The type of target value + */ +@FunctionalInterface +public interface ValueParser { + + TYPE parse( + @NotNull ConfigurationHolder holder, + @NotNull ValueType 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 new file mode 100644 index 0000000..37e74e0 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueSerializer.java @@ -0,0 +1,19 @@ +package cc.carm.lib.configuration.adapter; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.NotNull; + +/** + * Value serializer, convert target value to base data. + * + * @param The type of value + */ +@FunctionalInterface +public interface ValueSerializer { + + Object serialize( + @NotNull ConfigurationHolder holder, + @NotNull ValueType 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..3a889fa --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueType.java @@ -0,0 +1,190 @@ +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 { + + public static final ValueType STRING = ofPrimitiveType(String.class); + public static final ValueType INTEGER = ofPrimitiveType(Integer.class); + public static final ValueType INTEGER_TYPE = ofPrimitiveType(int.class); + public static final ValueType LONG = ofPrimitiveType(Long.class); + public static final ValueType LONG_TYPE = ofPrimitiveType(long.class); + public static final ValueType DOUBLE = ofPrimitiveType(Double.class); + public static final ValueType DOUBLE_TYPE = ofPrimitiveType(double.class); + public static final ValueType FLOAT = ofPrimitiveType(Float.class); + public static final ValueType FLOAT_TYPE = ofPrimitiveType(float.class); + public static final ValueType BOOLEAN = ofPrimitiveType(Boolean.class); + public static final ValueType BOOLEAN_TYPE = ofPrimitiveType(boolean.class); + public static final ValueType BYTE = ofPrimitiveType(Byte.class); + public static final ValueType BYTE_TYPE = ofPrimitiveType(byte.class); + public static final ValueType SHORT = ofPrimitiveType(Short.class); + public static final ValueType SHORT_TYPE = ofPrimitiveType(short.class); + public static final ValueType CHAR = ofPrimitiveType(Character.class); + public static final ValueType CHAR_TYPE = ofPrimitiveType(char.class); + + public static final ValueType[] PRIMITIVE_TYPES = { + STRING, INTEGER, LONG, DOUBLE, FLOAT, BOOLEAN, BYTE, SHORT, CHAR, + INTEGER_TYPE, LONG_TYPE, DOUBLE_TYPE, FLOAT_TYPE, BOOLEAN_TYPE, BYTE_TYPE, SHORT_TYPE, CHAR_TYPE + }; + + @SuppressWarnings("unchecked") + public static ValueType of(@NotNull T value) { + return of((Class) value.getClass()); + } + + @SuppressWarnings("unchecked") + public static ValueType of(final Type type) { + if (type == null) throw new NullPointerException("Type cannot be null"); + if (type instanceof Class) { // Try handle primitive types + Class clazz = (Class) type; + for (ValueType valueType : PRIMITIVE_TYPES) { + if (valueType.getRawType() == clazz) { + return (ValueType) valueType; + } + } + } + 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 static ValueType ofPrimitiveType(Class clazz) { + return new ValueType(clazz) { + }; + } + + 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); + } + if (obj instanceof Type) { + return Objects.equals(type, obj); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(type); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapter.java b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapter.java new file mode 100644 index 0000000..ecd3dd5 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapter.java @@ -0,0 +1,125 @@ +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.DataFunction; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; + +public class PrimitiveAdapter extends ValueAdapter { + + public static final PrimitiveAdapter[] ADAPTERS = new PrimitiveAdapter[]{ + ofString(), ofBoolean(), ofBooleanType(), ofCharacter(), ofCharacterType(), + ofInteger(), ofIntegerType(), ofLong(), ofLongType(), ofDouble(), ofDoubleType(), + ofFloat(), ofFloatType(), ofShort(), ofShortType(), ofByte(), ofByteType() + }; + + public static final String[] TRUE_VALUES = new String[]{ + "true", "yes", "on", "1", "enabled", "enable", "active" + }; + + public static final String[] FALSE_VALUES = new String[]{ + "false", "no", "off", "0", "disabled", "disable", "inactive" + }; + + @SuppressWarnings({"unchecked", "rawtypes"}) + public static ValueAdapter> ofEnum() { + ValueAdapter> adapter = new ValueAdapter<>(new ValueType>() { + }); + adapter.parser((provider, type, data) -> Enum.valueOf((Class) type.getRawType(), data.toString())); + adapter.serializer((provider, type, value) -> value.name()); + return adapter; + } + + public static PrimitiveAdapter ofString() { + return of(String.class, o -> o instanceof String ? (String) o : o.toString()); + } + + public static PrimitiveAdapter ofBoolean() { + return of(Boolean.class, data -> { + if (data instanceof Boolean) return (Boolean) data; + String v = data.toString().trim(); + if (Arrays.stream(TRUE_VALUES).anyMatch(v::equalsIgnoreCase)) return true; + else if (Arrays.stream(FALSE_VALUES).anyMatch(v::equalsIgnoreCase)) return false; + else throw new IllegalArgumentException("Cannot parse boolean from " + data); + }); + } + + public static PrimitiveAdapter ofBooleanType() { + return of(boolean.class, o -> o instanceof Boolean ? (Boolean) o : Boolean.parseBoolean(o.toString())); + } + + public static PrimitiveAdapter ofCharacter() { + return of(Character.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0)); + } + + public static PrimitiveAdapter ofCharacterType() { + return of(char.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0)); + } + + public static PrimitiveAdapter ofInteger() { + return ofNumber(Integer.class, Number::intValue, Integer::parseInt); + } + + public static PrimitiveAdapter ofIntegerType() { + return ofNumber(int.class, Number::intValue, Integer::parseInt); + } + + public static PrimitiveAdapter ofLong() { + return ofNumber(Long.class, Number::longValue, Long::parseLong); + } + + public static PrimitiveAdapter ofLongType() { + return ofNumber(long.class, Number::longValue, Long::parseLong); + } + + public static PrimitiveAdapter ofDouble() { + return ofNumber(Double.class, Number::doubleValue, Double::parseDouble); + } + + public static PrimitiveAdapter ofDoubleType() { + return ofNumber(double.class, Number::doubleValue, Double::parseDouble); + } + + public static PrimitiveAdapter ofFloat() { + return ofNumber(Float.class, Number::floatValue, Float::parseFloat); + } + + public static PrimitiveAdapter ofFloatType() { + return ofNumber(float.class, Number::floatValue, Float::parseFloat); + } + + public static PrimitiveAdapter ofShort() { + return ofNumber(Short.class, Number::shortValue, Short::parseShort); + } + + public static PrimitiveAdapter ofShortType() { + return ofNumber(short.class, Number::shortValue, Short::parseShort); + } + + public static PrimitiveAdapter ofByte() { + return ofNumber(Byte.class, Number::byteValue, Byte::parseByte); + } + + public static PrimitiveAdapter ofByteType() { + return ofNumber(byte.class, Number::byteValue, Byte::parseByte); + } + + public static PrimitiveAdapter of(@NotNull Class clazz, + @NotNull DataFunction function) { + return new PrimitiveAdapter<>(clazz, (p, type, data) -> function.handle(data)); + } + + public static PrimitiveAdapter ofNumber(@NotNull Class numberClass, + @NotNull DataFunction castFunction, + @NotNull DataFunction parseFunction) { + return of(numberClass, o -> o instanceof Number ? castFunction.handle((Number) o) : parseFunction.handle(o.toString())); + } + + protected PrimitiveAdapter(@NotNull Class valueType, @NotNull ValueParser deserializer) { + super(ValueType.of(valueType), (provider, type, value) -> value, deserializer); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/StandardAdapters.java b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/StandardAdapters.java new file mode 100644 index 0000000..4b332d5 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/StandardAdapters.java @@ -0,0 +1,19 @@ +package cc.carm.lib.configuration.adapter.strandard; + +import cc.carm.lib.configuration.adapter.ValueAdapter; +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.source.section.ConfigureSection; + +public interface StandardAdapters { + + ValueAdapter SECTION_ADAPTER = new ValueAdapter<>( + ValueType.of(ConfigureSection.class), + (provider, type, value) -> value, + (provider, type, value) -> { + if (value instanceof ConfigureSection) { + return (ConfigureSection) value; + } else throw new IllegalArgumentException("Value is not a ConfigurationSection"); + } + ); + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/annotation/ConfigPath.java b/core/src/main/java/cc/carm/lib/configuration/annotation/ConfigPath.java similarity index 81% rename from core/src/main/java/cc/carm/lib/configuration/core/annotation/ConfigPath.java rename to core/src/main/java/cc/carm/lib/configuration/annotation/ConfigPath.java index 25fee49..e84c4ac 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/annotation/ConfigPath.java +++ b/core/src/main/java/cc/carm/lib/configuration/annotation/ConfigPath.java @@ -1,6 +1,4 @@ -package cc.carm.lib.configuration.core.annotation; - -import cc.carm.lib.configuration.core.ConfigInitializer; +package cc.carm.lib.configuration.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -16,7 +14,7 @@ public @interface ConfigPath { /** * The path value of the current configuration. - * If not set,will generate the path by {@link ConfigInitializer#getPathFromName(String)}. + * If not set,will generate the path by {@link cc.carm.lib.configuration.source.loader.PathGenerator}. * * @return The path value of the current configuration */ diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/AbstractConfigBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/AbstractConfigBuilder.java new file mode 100644 index 0000000..a44d2cb --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/AbstractConfigBuilder.java @@ -0,0 +1,91 @@ +package cc.carm.lib.configuration.builder; + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder; +import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; +import cc.carm.lib.configuration.value.ConfigValue; +import cc.carm.lib.configuration.value.ValueManifest; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public abstract class AbstractConfigBuilder< + TYPE, RESULT extends ConfigValue, PROVIDER extends ConfigurationHolder, + SELF extends AbstractConfigBuilder + > { + + protected final Class providerClass; + protected final ValueType type; + + protected @Nullable PROVIDER provider; + protected @Nullable String path; + + protected @NotNull Supplier defaultValueSupplier = () -> null; + protected @NotNull BiConsumer, String> initializer = (provider, path) -> { + }; + + protected AbstractConfigBuilder(Class providerClass, ValueType type) { + this.providerClass = providerClass; + this.type = type; + } + + public @NotNull ValueType type() { + return type; + } + + protected abstract @NotNull SELF self(); + + public abstract @NotNull RESULT build(); + + public @NotNull SELF holder(@Nullable PROVIDER provider) { + this.provider = provider; + return self(); + } + + public @NotNull SELF path(@Nullable String path) { + this.path = path; + return self(); + } + + public @NotNull SELF initializer(@NotNull BiConsumer, String> initializer) { + this.initializer = initializer; + return self(); + } + + public @NotNull SELF append(@NotNull BiConsumer, String> initializer) { + return initializer(initializer.andThen(initializer)); + } + + public @NotNull SELF append(@NotNull Consumer> initializer) { + return append((provider, path) -> initializer.accept(provider)); + } + + public @NotNull SELF defaults(@Nullable TYPE defaultValue) { + return defaults(() -> defaultValue); + } + + public @NotNull SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) { + this.defaultValueSupplier = supplier; + return self(); + } + + public @NotNull SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) { + return append((provider, path) -> metaConsumer.accept(provider.metadata(path))); + } + + public @NotNull SELF meta(@NotNull ConfigurationMetadata type, @Nullable M value) { + return meta(holder -> holder.set(type, value)); + } + + protected @NotNull ValueManifest buildManifest() { + return new ValueManifest<>( + type(), this.defaultValueSupplier, this.initializer, + this.provider, this.path + ); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/CommonConfigBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/CommonConfigBuilder.java new file mode 100644 index 0000000..03dff8d --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/CommonConfigBuilder.java @@ -0,0 +1,14 @@ +package cc.carm.lib.configuration.builder; + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.value.ConfigValue; + +public abstract class CommonConfigBuilder, SELF extends CommonConfigBuilder> + extends AbstractConfigBuilder, SELF> { + + protected CommonConfigBuilder(ValueType type) { + super(ConfigurationHolder.class, type); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSectionBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSectionBuilder.java new file mode 100644 index 0000000..894a97f --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSectionBuilder.java @@ -0,0 +1,74 @@ +package cc.carm.lib.configuration.builder.impl; + +import cc.carm.lib.configuration.adapter.ValueAdapter; +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.builder.CommonConfigBuilder; +import cc.carm.lib.configuration.function.DataFunction; +import cc.carm.lib.configuration.function.ValueHandler; +import cc.carm.lib.configuration.source.section.ConfigureSection; +import cc.carm.lib.configuration.value.ConfigValue; +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Consumer; + +public abstract class AbstractSectionBuilder< + TYPE, PARAM, + RESULT extends ConfigValue, + SELF extends AbstractSectionBuilder + > extends CommonConfigBuilder { + + protected final @NotNull ValueType paramType; + + protected @NotNull ValueHandler parser; + protected @NotNull ValueHandler> serializer; + + public AbstractSectionBuilder(@NotNull ValueType type, @NotNull ValueType paramType, + @NotNull ValueHandler parser, + @NotNull ValueHandler> serializer) { + super(type); + this.paramType = paramType; + this.parser = parser; + this.serializer = serializer; + } + + public @NotNull SELF parse(DataFunction valueParser) { + return parse((p, section) -> valueParser.handle(section)); + } + + public @NotNull SELF parse(ValueHandler valueParser) { + this.parser = valueParser; + return self(); + } + + public @NotNull SELF serialize(DataFunction> serializer) { + return serialize((p, value) -> serializer.handle(value)); + } + + public @NotNull SELF serialize(ValueHandler> serializer) { + this.serializer = serializer; + return self(); + } + + public @NotNull SELF serialize(Consumer> serializer) { + return serialize((p, value) -> { + Map map = new LinkedHashMap<>(); + serializer.accept(map); + return map; + }); + } + + protected ValueAdapter buildAdapter() { + return new ValueAdapter<>(this.paramType) + .parser((p, type, data) -> { + ConfigureSection section = p.deserialize(ConfigureSection.class, data); + if (section == null) return null; + return this.parser.handle(p, section); + }) + .serializer((p, type, data) -> { + Map map = this.serializer.handle(p, data); + return map == null || map.isEmpty() ? null : map; + }); + } +} diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSourceBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSourceBuilder.java new file mode 100644 index 0000000..1a4c7b6 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSourceBuilder.java @@ -0,0 +1,63 @@ +package cc.carm.lib.configuration.builder.impl; + +import cc.carm.lib.configuration.adapter.ValueAdapter; +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.builder.CommonConfigBuilder; +import cc.carm.lib.configuration.function.DataFunction; +import cc.carm.lib.configuration.function.ValueHandler; +import cc.carm.lib.configuration.value.ConfigValue; +import org.jetbrains.annotations.NotNull; + +public abstract class AbstractSourceBuilder< + V, SOURCE, PARAM, RESULT extends ConfigValue, + SELF extends AbstractSourceBuilder + > extends CommonConfigBuilder { + + protected final @NotNull ValueType sourceType; + protected final @NotNull ValueType paramType; + protected @NotNull ValueHandler valueParser; + protected @NotNull ValueHandler valueSerializer; + + public AbstractSourceBuilder(@NotNull ValueType type, + @NotNull ValueType sourceType, @NotNull ValueType paramType, + @NotNull ValueHandler parser, + @NotNull ValueHandler serializer) { + super(type); + this.sourceType = sourceType; + this.paramType = paramType; + this.valueParser = parser; + this.valueSerializer = serializer; + } + + public @NotNull SELF parse(DataFunction parser) { + return parse((p, source) -> parser.handle(source)); + } + + public @NotNull SELF parse(@NotNull ValueHandler parser) { + this.valueParser = parser; + return self(); + } + + public @NotNull SELF serialize(@NotNull ValueHandler serializer) { + this.valueSerializer = serializer; + return self(); + } + + public @NotNull SELF serialize(@NotNull DataFunction serializer) { + return serialize((p, value) -> serializer.handle(value)); + } + + protected ValueAdapter buildAdapter() { + return new ValueAdapter<>(this.paramType) + .parser((holder, type, data) -> { + SOURCE source = holder.deserialize(this.sourceType, data); + return this.valueParser.handle(holder, source); + }) + .serializer((holder, type, data) -> { + SOURCE source = this.valueSerializer.handle(holder, data); + return holder.serialize(source); + }); + } + + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/list/ConfigListBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/list/ConfigListBuilder.java new file mode 100644 index 0000000..c2099da --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/list/ConfigListBuilder.java @@ -0,0 +1,34 @@ +package cc.carm.lib.configuration.builder.list; + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.function.ValueHandler; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; + +public class ConfigListBuilder { + + protected final @NotNull ValueType type; + + public ConfigListBuilder(@NotNull ValueType type) { + this.type = type; + } + + public @NotNull SourceListBuilder from(@NotNull Class sourceType) { + return from(ValueType.of(sourceType)); + } + + public @NotNull SourceListBuilder from(@NotNull ValueType sourceType) { + return new SourceListBuilder<>(sourceType, type, ValueHandler.required(), ValueHandler.required(), ArrayList::new); + } + + public @NotNull SourceListBuilder fromString() { + return new SourceListBuilder<>(ValueType.STRING, type, ValueHandler.required(), ValueHandler.stringValue(), ArrayList::new); + } + + public @NotNull SectionListBuilder fromSection() { + return new SectionListBuilder<>(type, ValueHandler.required(), ValueHandler.required(), ArrayList::new); + } + + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/list/SectionListBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/list/SectionListBuilder.java new file mode 100644 index 0000000..356a0b9 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/list/SectionListBuilder.java @@ -0,0 +1,53 @@ +package cc.carm.lib.configuration.builder.list; + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder; +import cc.carm.lib.configuration.function.ValueHandler; +import cc.carm.lib.configuration.source.section.ConfigureSection; +import cc.carm.lib.configuration.value.standard.ConfiguredList; +import org.jetbrains.annotations.NotNull; + +import java.util.*; +import java.util.function.Supplier; + +public class SectionListBuilder extends AbstractSectionBuilder, V, ConfiguredList, SectionListBuilder> { + + protected @NotNull Supplier> constructor; + + public SectionListBuilder(@NotNull ValueType paramType, + @NotNull ValueHandler parser, + @NotNull ValueHandler> serializer, + @NotNull Supplier> constructor) { + super(new ValueType>() { + }, paramType, parser, serializer); + this.constructor = constructor; + } + + @SafeVarargs + public final @NotNull SectionListBuilder defaults(@NotNull V... values) { + return defaults(new ArrayList<>(Arrays.asList(values))); + } + + public final @NotNull SectionListBuilder defaults(@NotNull Collection values) { + return defaults(new ArrayList<>(values)); + } + + public SectionListBuilder constructor(@NotNull Supplier> constructor) { + this.constructor = constructor; + return this; + } + + public > SectionListBuilder construct(@NotNull LIST list) { + return constructor(() -> list); + } + + @Override + protected @NotNull SectionListBuilder self() { + return this; + } + + @Override + public @NotNull ConfiguredList build() { + return new ConfiguredList<>(buildManifest(), constructor, buildAdapter()); + } +} diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/list/SourceListBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/list/SourceListBuilder.java new file mode 100644 index 0000000..655114c --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/list/SourceListBuilder.java @@ -0,0 +1,47 @@ +package cc.carm.lib.configuration.builder.list; + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder; +import cc.carm.lib.configuration.function.ValueHandler; +import cc.carm.lib.configuration.value.standard.ConfiguredList; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.Supplier; + +public class SourceListBuilder + extends AbstractSourceBuilder, SOURCE, V, ConfiguredList, SourceListBuilder> { + + protected @NotNull Supplier> constructor; + + public SourceListBuilder(@NotNull ValueType sourceType, @NotNull ValueType paramType, + @NotNull ValueHandler parser, @NotNull ValueHandler serializer, + @NotNull Supplier> constructor) { + super(new ValueType>() { + }, sourceType, paramType, parser, serializer); + this.constructor = constructor; + } + + @SafeVarargs + public final @NotNull SourceListBuilder defaults(@NotNull V... values) { + return defaults(new ArrayList<>(Arrays.asList(values))); + } + + public final @NotNull SourceListBuilder defaults(@NotNull Collection values) { + return defaults(new ArrayList<>(values)); + } + + @Override + protected @NotNull SourceListBuilder self() { + return this; + } + + @Override + public @NotNull ConfiguredList build() { + return new ConfiguredList<>(buildManifest(), this.constructor, buildAdapter()); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/value/ConfigValueBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/value/ConfigValueBuilder.java new file mode 100644 index 0000000..cad8557 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/value/ConfigValueBuilder.java @@ -0,0 +1,47 @@ +package cc.carm.lib.configuration.builder.value; + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.function.ValueHandler; +import cc.carm.lib.configuration.source.section.ConfigureSection; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class ConfigValueBuilder { + + protected final @NotNull ValueType type; + + public ConfigValueBuilder(@NotNull ValueType type) { + this.type = type; + } + + public @NotNull SourceValueBuilder from(@NotNull Class clazz) { + return from(ValueType.of(clazz)); + } + + public @NotNull SourceValueBuilder from(@NotNull ValueType sourceType) { + return from(sourceType, ValueHandler.required(), ValueHandler.required()); + } + + public @NotNull SourceValueBuilder from(@NotNull ValueType sourceType, + @NotNull ValueHandler valueParser, + @NotNull ValueHandler valueSerializer) { + return new SourceValueBuilder<>(sourceType, this.type, valueParser, valueSerializer); + } + + public @NotNull SourceValueBuilder fromString() { + return from(ValueType.STRING, ValueHandler.required(), ValueHandler.stringValue()); + } + + public @NotNull SectionValueBuilder fromSection() { + return fromSection(ValueHandler.required(), ValueHandler.required()); + } + + public @NotNull SectionValueBuilder fromSection( + @NotNull ValueHandler valueParser, + @NotNull ValueHandler> valueSerializer + ) { + return new SectionValueBuilder<>(this.type, valueParser, valueSerializer); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/value/SectionValueBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/value/SectionValueBuilder.java new file mode 100644 index 0000000..f9653e8 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/value/SectionValueBuilder.java @@ -0,0 +1,30 @@ +package cc.carm.lib.configuration.builder.value; + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder; +import cc.carm.lib.configuration.function.ValueHandler; +import cc.carm.lib.configuration.source.section.ConfigureSection; +import cc.carm.lib.configuration.value.standard.ConfiguredValue; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class SectionValueBuilder extends AbstractSectionBuilder, SectionValueBuilder> { + + public SectionValueBuilder(@NotNull ValueType type, + @NotNull ValueHandler parser, + @NotNull ValueHandler> serializer) { + super(type, type, parser, serializer); + } + + @Override + protected @NotNull SectionValueBuilder self() { + return this; + } + + @Override + public @NotNull ConfiguredValue build() { + return ConfiguredValue.of(buildManifest(), buildAdapter()); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/value/SourceValueBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/value/SourceValueBuilder.java new file mode 100644 index 0000000..96b01b0 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/builder/value/SourceValueBuilder.java @@ -0,0 +1,27 @@ +package cc.carm.lib.configuration.builder.value; + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder; +import cc.carm.lib.configuration.function.ValueHandler; +import cc.carm.lib.configuration.value.standard.ConfiguredValue; +import org.jetbrains.annotations.NotNull; + +public class SourceValueBuilder extends AbstractSourceBuilder, SourceValueBuilder> { + + + public SourceValueBuilder(@NotNull ValueType sourceType, @NotNull ValueType valueType, + @NotNull ValueHandler parser, @NotNull ValueHandler serializer) { + super(valueType, sourceType, valueType, parser, serializer); + } + + @Override + protected @NotNull SourceValueBuilder self() { + return this; + } + + @Override + public @NotNull ConfiguredValue build() { + return new ConfiguredValue<>(buildManifest(), buildAdapter()); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/ConfigInitializer.java b/core/src/main/java/cc/carm/lib/configuration/core/ConfigInitializer.java deleted file mode 100644 index 1ea3f22..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/ConfigInitializer.java +++ /dev/null @@ -1,275 +0,0 @@ -package cc.carm.lib.configuration.core; - -import cc.carm.lib.configuration.core.annotation.ConfigPath; -import cc.carm.lib.configuration.core.annotation.HeaderComment; -import cc.carm.lib.configuration.core.annotation.InlineComment; -import cc.carm.lib.configuration.core.source.ConfigurationProvider; -import cc.carm.lib.configuration.core.value.ConfigValue; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.List; - -/** - * 配置文件类初始化方法 - * 用于初始化 {@link Configuration} 中的每个 {@link ConfigValue} 对象 - * - * @param {@link ConfigurationProvider} 配置文件的数据来源 - * @author CarmJos - */ -public class ConfigInitializer> { - - protected final @NotNull T provider; - - public ConfigInitializer(@NotNull T provider) { - this.provider = provider; - } - - /** - * 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。 - * - * @param clazz 配置文件类,须继承于 {@link Configuration} 。 - * @param saveDefaults 是否写入默认值(默认为 true)。 - */ - public void initialize(@NotNull Class clazz, boolean saveDefaults) { - initialize(clazz, saveDefaults, true); - } - - /** - * 初始化指定类的所有 {@link ConfigValue} 对象。 - * - * @param clazz 配置文件类,须继承于 {@link Configuration} 。 - * @param saveDefaults 是否写入默认值(默认为 true)。 - * @param loadSubClasses 是否加载内部子类(默认为 true)。 - */ - public void initialize(@NotNull Class clazz, - boolean saveDefaults, boolean loadSubClasses) { - initializeStaticClass( - clazz, null, null, - null, null, null, - saveDefaults, loadSubClasses - ); - if (saveDefaults) { - try { - provider.save(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - /** - * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。 - * - * @param config 配置文件实例类,须实现 {@link Configuration} 。 - */ - public void initialize(@NotNull Configuration config) { - initialize(config, true); - } - - /** - * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。 - * - * @param config 配置文件实例类,须实现 {@link Configuration} 。 - * @param saveDefaults 是否写入默认值(默认为 true)。 - */ - public void initialize(@NotNull Configuration config, boolean saveDefaults) { - initializeInstance( - config, null, null, - null, null, null, - saveDefaults - ); - if (saveDefaults) { - try { - provider.save(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - - // 针对实例类的初始化方法 - private void initializeInstance(@NotNull Configuration root, - @Nullable String parentPath, @Nullable String fieldName, - @Nullable ConfigPath fieldPath, - @Nullable HeaderComment fieldHeaderComments, - @Nullable InlineComment fieldInlineComments, - boolean saveDefaults) { - String path = getClassPath(root.getClass(), parentPath, fieldName, fieldPath); - this.provider.setHeaderComment(path, getClassHeaderComments(root.getClass(), fieldHeaderComments)); - if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments)); - - for (Field field : root.getClass().getDeclaredFields()) { - initializeField(root, field, path, saveDefaults, false); - } - } - - // 针对静态类的初始化方法 - private void initializeStaticClass(@NotNull Class clazz, - @Nullable String parentPath, @Nullable String fieldName, - @Nullable ConfigPath fieldPath, - @Nullable HeaderComment fieldHeaderComments, - @Nullable InlineComment fieldInlineComments, - boolean saveDefaults, boolean loadSubClasses) { - if (!Configuration.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类 - String path = getClassPath(clazz, parentPath, fieldName, fieldPath); - this.provider.setHeaderComment(path, getClassHeaderComments(clazz, fieldHeaderComments)); - if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments)); - - for (Field field : clazz.getDeclaredFields()) { - initializeField(clazz, field, path, saveDefaults, loadSubClasses); - } - - if (!loadSubClasses) return; - Class[] classes = clazz.getDeclaredClasses(); - for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。 - initializeStaticClass( - classes[i], path, classes[i].getSimpleName(), - null, null, null, - saveDefaults, true - ); - } - } - - private void initializeField(@NotNull Object source, @NotNull Field field, - @Nullable String parent, boolean saveDefaults, boolean loadSubClasses) { - try { - field.setAccessible(true); - Object object = field.get(source); - - if (object instanceof ConfigValue) { - initializeValue( - (ConfigValue) object, getFieldPath(field, parent), - field.getAnnotation(HeaderComment.class), - field.getAnnotation(InlineComment.class), - saveDefaults - ); - } else if (source instanceof Configuration && object instanceof Configuration) { - // 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。 - initializeInstance( - (Configuration) object, parent, field.getName(), - field.getAnnotation(ConfigPath.class), - field.getAnnotation(HeaderComment.class), - field.getAnnotation(InlineComment.class), - saveDefaults - ); - } else if (source instanceof Class && object instanceof Class) { - // 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。 - initializeStaticClass( - (Class) object, parent, field.getName(), - field.getAnnotation(ConfigPath.class), - field.getAnnotation(HeaderComment.class), - field.getAnnotation(InlineComment.class), - saveDefaults, loadSubClasses - ); - } - - // 以上判断实现以下规范: - // - 实例类中仅加载 ConfigValue实例 与 ConfigurationRoot实例 - // - 静态类中仅加载 静态ConfigValue实例 与 静态ConfigurationRoot类 - - } catch (IllegalAccessException ignored) { - } - } - - protected void initializeValue(@NotNull ConfigValue value, @NotNull String path, - @Nullable HeaderComment fieldHeaderComment, - @Nullable InlineComment fieldInlineComment, - boolean saveDefaults) { - value.initialize( - provider, saveDefaults, path, - readHeaderComments(fieldHeaderComment), - readInlineComments(fieldInlineComment) - ); - } - - protected static @Nullable List getClassHeaderComments(@NotNull Class clazz, - @Nullable HeaderComment fieldAnnotation) { - List classComments = readHeaderComments(clazz.getAnnotation(HeaderComment.class)); - if (classComments != null) return classComments; - else return readHeaderComments(fieldAnnotation); - } - - - protected static List readHeaderComments(@Nullable HeaderComment annotation) { - if (annotation == null) return null; - String[] value = annotation.value(); - return value.length > 0 ? Arrays.asList(value) : null; - } - - - protected static @Nullable String readInlineComments(@Nullable InlineComment annotation) { - if (annotation == null) return null; - String value = annotation.value(); - return value.length() > 0 ? value : null; - } - - protected static @Nullable String getClassPath(@Nullable Class clazz, - @Nullable String parentPath, - @Nullable String filedName, - @Nullable ConfigPath fieldAnnotation) { - @NotNull String parent = parentPath != null ? parentPath + "." : ""; - boolean fromRoot = false; - - // 先获取 Class 对应的路径注解 其优先度最高。 - if (clazz != null) { - ConfigPath clazzAnnotation = clazz.getAnnotation(ConfigPath.class); - if (clazzAnnotation != null) { - fromRoot = clazzAnnotation.root(); - if (clazzAnnotation.value().length() > 0) { - return (fromRoot ? "" : parent) + clazzAnnotation.value(); - } - } - } - - if (fieldAnnotation != null) { - fromRoot = fromRoot || fieldAnnotation.root(); - if (fieldAnnotation.value().length() > 0) { - return (fromRoot ? "" : parent) + fieldAnnotation.value(); - } - } - - // 再由 fieldName 获取路径 - if (filedName != null) return (fromRoot ? "" : parent) + getPathFromName(filedName); - else return null; // 不满足上述条件 且 无 fieldName,则说明是根路径。 - } - - protected static @NotNull String getFieldPath(@NotNull Field field, @Nullable String parentPath) { - @NotNull String parent = parentPath != null ? parentPath + "." : ""; - boolean fromRoot = false; - - // 先获取 Field 对应的路径注解 其优先度最高。 - ConfigPath pathAnnotation = field.getAnnotation(ConfigPath.class); - if (pathAnnotation != null) { - fromRoot = pathAnnotation.root(); - if (pathAnnotation.value().length() > 0) { - return (fromRoot ? "" : parent) + pathAnnotation.value(); - } - } - - // 最后再通过 fieldName 自动生成路径 - return (fromRoot ? "" : parent) + getPathFromName(field.getName()); - } - - /** - * 得到指定元素的配置名称。 - * 采用 全小写,以“-”链接 的命名规则。 - * - * @param name 源名称 - * @return 全小写,以“-”链接 的 路径名称 - */ - public static String getPathFromName(String name) { - return name.replaceAll("[A-Z]", "-$0") // 将驼峰转换为蛇形; - .replaceAll("-(.*)", "$1") // 若首字母也为大写,则也会被转换,需要去掉第一个横线 - .replaceAll("_-([A-Z])", "_$1") // 因为命名中可能包含 _,因此需要被特殊处理一下 - .replaceAll("([a-z])-([A-Z])", "$1_$2") // 然后将非全大写命名的内容进行转换 - .replace("-", "") // 移除掉多余的横线 - .replace("_", "-") // 将下划线替换为横线 - .toLowerCase(); // 最后转为全小写 - } - - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/Configuration.java b/core/src/main/java/cc/carm/lib/configuration/core/Configuration.java deleted file mode 100644 index 1ba41b9..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/Configuration.java +++ /dev/null @@ -1,8 +0,0 @@ -package cc.carm.lib.configuration.core; - -/** - * The root interface of the configuration file interfaces, - * which is used to label and record the configuration information. - */ -public interface Configuration { -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/ConfigurationRoot.java b/core/src/main/java/cc/carm/lib/configuration/core/ConfigurationRoot.java deleted file mode 100644 index 8197290..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/ConfigurationRoot.java +++ /dev/null @@ -1,8 +0,0 @@ -package cc.carm.lib.configuration.core; - -/** - * The root node of the configuration file class, - * which is used to label and record the configuration information. - */ -public abstract class ConfigurationRoot implements Configuration { -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/AbstractConfigBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/AbstractConfigBuilder.java deleted file mode 100644 index eef1122..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/AbstractConfigBuilder.java +++ /dev/null @@ -1,77 +0,0 @@ -package cc.carm.lib.configuration.core.builder; - -import cc.carm.lib.configuration.core.source.ConfigurationProvider; -import cc.carm.lib.configuration.core.value.ConfigValue; -import cc.carm.lib.configuration.core.value.ValueManifest; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Arrays; -import java.util.List; -import java.util.function.Supplier; - -public abstract class AbstractConfigBuilder, P extends ConfigurationProvider> { - - protected final Class providerClass; - - protected @Nullable P provider; - protected @Nullable String path; - - protected @Nullable List headerComments; - protected @Nullable String inlineComment; - - protected @Nullable T defaultValue; - - protected AbstractConfigBuilder(Class providerClass) { - this.providerClass = providerClass; - } - - protected abstract @NotNull B getThis(); - - public abstract @NotNull ConfigValue build(); - - public @NotNull B from(@Nullable P provider) { - this.provider = provider; - return getThis(); - } - - public @NotNull B path(@Nullable String path) { - this.path = path; - return getThis(); - } - - public @NotNull B comments(@NotNull String... comments) { - return headerComments(comments); - } - - public @NotNull B headerComments(@NotNull String... comments) { - return headerComments(Arrays.asList(comments)); - } - - public @NotNull B headerComments(@NotNull List comments) { - this.headerComments = comments; - return getThis(); - } - - public @NotNull B inlineComment(@NotNull String comment) { - this.inlineComment = comment; - return getThis(); - } - - public @NotNull B defaults(@Nullable T defaultValue) { - this.defaultValue = defaultValue; - return getThis(); - } - - public @NotNull B defaults(@NotNull Supplier<@Nullable T> defaultValueSupplier) { - return defaults(defaultValueSupplier.get()); - } - - protected @NotNull ValueManifest buildManifest() { - return ValueManifest.of( - this.provider, this.path, - this.headerComments, this.inlineComment, this.defaultValue - ); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/CommonConfigBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/CommonConfigBuilder.java deleted file mode 100644 index 941fdcd..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/CommonConfigBuilder.java +++ /dev/null @@ -1,12 +0,0 @@ -package cc.carm.lib.configuration.core.builder; - -import cc.carm.lib.configuration.core.source.ConfigurationProvider; - -public abstract class CommonConfigBuilder> - extends AbstractConfigBuilder> { - - protected CommonConfigBuilder() { - super(ConfigurationProvider.class); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/ConfigBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/ConfigBuilder.java deleted file mode 100644 index 68262df..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/ConfigBuilder.java +++ /dev/null @@ -1,43 +0,0 @@ -package cc.carm.lib.configuration.core.builder; - -import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder; -import cc.carm.lib.configuration.core.builder.map.ConfigMapBuilder; -import cc.carm.lib.configuration.core.builder.map.ConfigMapCreator; -import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder; -import org.jetbrains.annotations.NotNull; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.TreeMap; - -public class ConfigBuilder { - - public @NotNull ConfigValueBuilder asValue(@NotNull Class valueClass) { - return new ConfigValueBuilder<>(valueClass); - } - - public @NotNull ConfigListBuilder asList(@NotNull Class valueClass) { - return new ConfigListBuilder<>(valueClass); - } - - public @NotNull ConfigMapCreator asMap(@NotNull Class keyClass, - @NotNull Class valueClass) { - return new ConfigMapCreator<>(keyClass, valueClass); - } - - public @NotNull ConfigMapBuilder, K, V> asHashMap(@NotNull Class keyClass, - @NotNull Class valueClass) { - return asMap(keyClass, valueClass).asHashMap(); - } - - public @NotNull ConfigMapBuilder, K, V> asLinkedMap(@NotNull Class keyClass, - @NotNull Class valueClass) { - return asMap(keyClass, valueClass).asLinkedMap(); - } - - public , V> @NotNull ConfigMapBuilder, K, V> asTreeMap(@NotNull Class keyClass, - @NotNull Class valueClass) { - return asMap(keyClass, valueClass).asTreeMap(); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/list/ConfigListBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/list/ConfigListBuilder.java deleted file mode 100644 index 94e5a44..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/list/ConfigListBuilder.java +++ /dev/null @@ -1,58 +0,0 @@ -package cc.carm.lib.configuration.core.builder.list; - -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; - - -public class ConfigListBuilder { - - protected final @NotNull Class valueClass; - - public ConfigListBuilder(@NotNull Class valueClass) { - this.valueClass = valueClass; - } - - public @NotNull SourceListBuilder from(@NotNull Class sourceClass, - @NotNull ConfigDataFunction sourceParser, - @NotNull ConfigDataFunction valueParser, - @NotNull ConfigDataFunction valueSerializer, - @NotNull ConfigDataFunction sourceSerializer) { - return new SourceListBuilder<>(sourceClass, sourceParser, this.valueClass, valueParser, valueSerializer, sourceSerializer); - } - - - public @NotNull SourceListBuilder from(Class sourceClass) { - return from(sourceClass, - ConfigDataFunction.required(), ConfigDataFunction.required(), - ConfigDataFunction.required(), ConfigDataFunction.required() - ); - } - - public @NotNull SourceListBuilder fromObject() { - return from( - Object.class, ConfigDataFunction.identity(), - ConfigDataFunction.castObject(valueClass), - ConfigDataFunction.toObject(), ConfigDataFunction.toObject() - ); - } - - public @NotNull SourceListBuilder fromString() { - return from( - String.class, ConfigDataFunction.castToString(), - ConfigDataFunction.castFromString(this.valueClass), - ConfigDataFunction.castToString(), ConfigDataFunction.toObject() - ); - } - - public @NotNull SourceListBuilder, V> fromMap() { - return from( - Map.class, obj -> (Map) obj, - ConfigDataFunction.required(), - ConfigDataFunction.required(), - ConfigDataFunction.toObject() - ); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/list/SourceListBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/list/SourceListBuilder.java deleted file mode 100644 index 81ce353..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/list/SourceListBuilder.java +++ /dev/null @@ -1,80 +0,0 @@ -package cc.carm.lib.configuration.core.builder.list; - -import cc.carm.lib.configuration.core.builder.CommonConfigBuilder; -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.value.type.ConfiguredList; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -public class SourceListBuilder extends CommonConfigBuilder, SourceListBuilder> { - - protected final @NotNull Class sourceClass; - protected @NotNull ConfigDataFunction sourceParser; - - protected final @NotNull Class valueClass; - protected @NotNull ConfigDataFunction valueParser; - - protected @NotNull ConfigDataFunction valueSerializer; - protected @NotNull ConfigDataFunction sourceSerializer; - - public SourceListBuilder(@NotNull Class sourceClass, @NotNull ConfigDataFunction sourceParser, - @NotNull Class valueClass, @NotNull ConfigDataFunction valueParser, - @NotNull ConfigDataFunction valueSerializer, - @NotNull ConfigDataFunction sourceSerializer) { - this.sourceClass = sourceClass; - this.sourceParser = sourceParser; - this.sourceSerializer = sourceSerializer; - this.valueClass = valueClass; - this.valueParser = valueParser; - this.valueSerializer = valueSerializer; - } - - @SafeVarargs - public final @NotNull SourceListBuilder defaults(@NotNull V... values) { - return defaults(new ArrayList<>(Arrays.asList(values))); - } - - public final @NotNull SourceListBuilder defaults(@NotNull Collection values) { - return defaults(new ArrayList<>(values)); - } - - public @NotNull SourceListBuilder parseSource(ConfigDataFunction sourceParser) { - this.sourceParser = sourceParser; - return this; - } - - public @NotNull SourceListBuilder parseValue(ConfigDataFunction valueParser) { - this.valueParser = valueParser; - return this; - } - - public @NotNull SourceListBuilder serializeValue(ConfigDataFunction serializer) { - this.valueSerializer = serializer; - return this; - } - - public @NotNull SourceListBuilder serializeSource(ConfigDataFunction serializer) { - this.sourceSerializer = serializer; - return this; - } - - @Override - protected @NotNull SourceListBuilder getThis() { - return this; - } - - @Override - public @NotNull ConfiguredList build() { - return new ConfiguredList<>( - buildManifest(), this.valueClass, - this.sourceParser.andThen(this.valueParser), - this.valueSerializer.andThen(sourceSerializer) - ); - } - - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/map/ConfigMapBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/map/ConfigMapBuilder.java deleted file mode 100644 index ecfa675..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/map/ConfigMapBuilder.java +++ /dev/null @@ -1,68 +0,0 @@ -package cc.carm.lib.configuration.core.builder.map; - -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; -import java.util.function.Supplier; - -public class ConfigMapBuilder, K, V> { - - protected final @NotNull Supplier supplier; - - protected final @NotNull Class keyClass; - protected final @NotNull Class valueClass; - - public ConfigMapBuilder(@NotNull Supplier supplier, @NotNull Class keyClass, @NotNull Class valueClass) { - this.supplier = supplier; - this.keyClass = keyClass; - this.valueClass = valueClass; - } - - public > ConfigMapBuilder supplier(@NotNull Supplier supplier) { - return new ConfigMapBuilder<>(supplier, keyClass, valueClass); - } - - public SourceMapBuilder from(@NotNull Class sourceClass, - @NotNull ConfigDataFunction valueParser, - @NotNull ConfigDataFunction valueSerializer) { - return new SourceMapBuilder<>(supplier, - keyClass, ConfigDataFunction.castFromString(this.keyClass), // #String -> key - sourceClass, ConfigDataFunction.castObject(sourceClass), // #Object -> source - valueClass, valueParser, // source -> value - ConfigDataFunction.castToString(), // key -> #String - valueSerializer/*value -> source*/, - ConfigDataFunction.toObject()/* source -> #Object */ - ); - } - - public SourceMapBuilder from(@NotNull Class sourceClass) { - return from(sourceClass, ConfigDataFunction.required(), ConfigDataFunction.required()); - } - - public SourceMapBuilder fromString(@NotNull ConfigDataFunction valueParser) { - return from(String.class, valueParser, ConfigDataFunction.castToString()); - } - - public SourceMapBuilder fromString() { - return fromString(ConfigDataFunction.castFromString(this.valueClass)); - } - - public SectionMapBuilder fromSection() { - return new SectionMapBuilder<>( - supplier, - keyClass, ConfigDataFunction.castFromString(keyClass), - valueClass, ConfigDataFunction.required(), - ConfigDataFunction.castToString(), ConfigDataFunction.required()); - } - - public SourceMapBuilder fromObject(@NotNull ConfigDataFunction valueParser) { - return from(Object.class, valueParser, ConfigDataFunction.toObject()); - } - - public SourceMapBuilder fromObject() { - return fromObject(ConfigDataFunction.required()); - } - - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/map/ConfigMapCreator.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/map/ConfigMapCreator.java deleted file mode 100644 index 4b92a5b..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/map/ConfigMapCreator.java +++ /dev/null @@ -1,38 +0,0 @@ -package cc.carm.lib.configuration.core.builder.map; - -import org.jetbrains.annotations.NotNull; - -import java.util.*; -import java.util.function.Supplier; - -public class ConfigMapCreator { - - protected final @NotNull Class keyClass; - protected final @NotNull Class valueClass; - - public ConfigMapCreator(@NotNull Class keyClass, @NotNull Class valueClass) { - this.keyClass = keyClass; - this.valueClass = valueClass; - } - - public > @NotNull ConfigMapBuilder asMap(Supplier mapSuppler) { - return new ConfigMapBuilder<>(mapSuppler, keyClass, valueClass); - } - - public @NotNull ConfigMapBuilder, K, V> asHashMap() { - return asMap(HashMap::new); - } - - public @NotNull ConfigMapBuilder, K, V> asLinkedMap() { - return asMap(LinkedHashMap::new); - } - - public @NotNull ConfigMapBuilder, K, V> asTreeMap() { - return asMap(TreeMap::new); - } - - public @NotNull ConfigMapBuilder, K, V> asTreeMap(@NotNull Comparator comparator) { - return asMap(() -> new TreeMap<>(comparator)); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/map/SectionMapBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/map/SectionMapBuilder.java deleted file mode 100644 index e94fdbe..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/map/SectionMapBuilder.java +++ /dev/null @@ -1,99 +0,0 @@ -package cc.carm.lib.configuration.core.builder.map; - -import cc.carm.lib.configuration.core.builder.CommonConfigBuilder; -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import cc.carm.lib.configuration.core.value.ValueManifest; -import cc.carm.lib.configuration.core.value.type.ConfiguredSectionMap; -import org.jetbrains.annotations.NotNull; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class SectionMapBuilder, K, V> extends CommonConfigBuilder> { - - protected final @NotNull Supplier supplier; - - protected final @NotNull Class keyClass; - protected @NotNull ConfigDataFunction keyParser; - - protected final @NotNull Class valueClass; - protected @NotNull ConfigDataFunction, V> valueParser; - - protected @NotNull ConfigDataFunction keySerializer; - protected @NotNull ConfigDataFunction> valueSerializer; - - public SectionMapBuilder(@NotNull Supplier supplier, - @NotNull Class keyClass, @NotNull ConfigDataFunction keyParser, - @NotNull Class valueClass, @NotNull ConfigDataFunction, V> valueParser, - @NotNull ConfigDataFunction keySerializer, - @NotNull ConfigDataFunction> valueSerializer) { - this.supplier = supplier; - this.keyClass = keyClass; - this.keyParser = keyParser; - this.valueClass = valueClass; - this.valueParser = valueParser; - this.keySerializer = keySerializer; - this.valueSerializer = valueSerializer; - } - - public > SectionMapBuilder supplier(@NotNull Supplier supplier) { - return new SectionMapBuilder<>(supplier, - keyClass, keyParser, valueClass, valueParser, keySerializer, valueSerializer - ); - } - - public @NotNull SectionMapBuilder defaults(@NotNull Consumer factory) { - M map = supplier.get(); - factory.accept(map); - return defaults(map); - } - - public @NotNull SectionMapBuilder parseKey(@NotNull ConfigDataFunction parser) { - this.keyParser = parser; - return this; - } - - public @NotNull SectionMapBuilder parseValue(@NotNull ConfigDataFunction, V> parser) { - this.valueParser = parser; - return this; - } - - public @NotNull SectionMapBuilder serializeKey(@NotNull ConfigDataFunction serializer) { - this.keySerializer = serializer; - return this; - } - - public @NotNull SectionMapBuilder serializeValue(@NotNull ConfigDataFunction> serializer) { - this.valueSerializer = serializer; - return this; - } - - public @NotNull SectionMapBuilder serializeValue(@NotNull BiConsumer> serializer) { - return serializeValue(v -> { - Map map = new LinkedHashMap<>(); - serializer.accept(v, map); - return map; - }); - } - - - @Override - protected @NotNull SectionMapBuilder getThis() { - return this; - } - - @Override - public @NotNull ConfiguredSectionMap build() { - return new ConfiguredSectionMap<>( - new ValueManifest<>(provider, path, headerComments, inlineComment, defaultValue), - this.supplier, this.keyClass, this.keyParser, - this.valueClass, this.valueParser, - this.keySerializer, this.valueSerializer - ); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/map/SourceMapBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/map/SourceMapBuilder.java deleted file mode 100644 index 90d6636..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/map/SourceMapBuilder.java +++ /dev/null @@ -1,107 +0,0 @@ -package cc.carm.lib.configuration.core.builder.map; - -import cc.carm.lib.configuration.core.builder.CommonConfigBuilder; -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.value.ValueManifest; -import cc.carm.lib.configuration.core.value.type.ConfiguredMap; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; -import java.util.function.Consumer; -import java.util.function.Supplier; - -public class SourceMapBuilder, S, K, V> extends CommonConfigBuilder> { - - protected final @NotNull Supplier supplier; - - protected final @NotNull Class keyClass; - protected @NotNull ConfigDataFunction keyParser; - - protected final @NotNull Class sourceClass; - protected @NotNull ConfigDataFunction sourceParser; - - protected final @NotNull Class valueClass; - protected @NotNull ConfigDataFunction valueParser; - - protected @NotNull ConfigDataFunction keySerializer; - protected @NotNull ConfigDataFunction valueSerializer; - protected @NotNull ConfigDataFunction sourceSerializer; - - public SourceMapBuilder(@NotNull Supplier supplier, - @NotNull Class keyClass, @NotNull ConfigDataFunction keyParser, - @NotNull Class sourceClass, @NotNull ConfigDataFunction sourceParser, - @NotNull Class valueClass, @NotNull ConfigDataFunction valueParser, - @NotNull ConfigDataFunction keySerializer, - @NotNull ConfigDataFunction valueSerializer, - @NotNull ConfigDataFunction sourceSerializer) { - this.supplier = supplier; - this.keyClass = keyClass; - this.keyParser = keyParser; - this.valueClass = valueClass; - this.valueParser = valueParser; - this.sourceClass = sourceClass; - this.sourceParser = sourceParser; - this.keySerializer = keySerializer; - this.valueSerializer = valueSerializer; - this.sourceSerializer = sourceSerializer; - } - - public > SourceMapBuilder supplier(@NotNull Supplier supplier) { - return new SourceMapBuilder<>(supplier, - keyClass, keyParser, sourceClass, sourceParser, valueClass, valueParser, - keySerializer, valueSerializer, sourceSerializer - ); - } - - public @NotNull SourceMapBuilder defaults(@NotNull Consumer factory) { - M map = supplier.get(); - factory.accept(map); - return defaults(map); - } - - public @NotNull SourceMapBuilder parseKey(@NotNull ConfigDataFunction parser) { - this.keyParser = parser; - return this; - } - - public @NotNull SourceMapBuilder parseSource(@NotNull ConfigDataFunction parser) { - this.sourceParser = parser; - return this; - } - - public @NotNull SourceMapBuilder parseValue(@NotNull ConfigDataFunction parser) { - this.valueParser = parser; - return this; - } - - public @NotNull SourceMapBuilder serializeKey(@NotNull ConfigDataFunction serializer) { - this.keySerializer = serializer; - return this; - } - - public @NotNull SourceMapBuilder serializeValue(@NotNull ConfigDataFunction serializer) { - this.valueSerializer = serializer; - return this; - } - - public @NotNull SourceMapBuilder serializeSource(@NotNull ConfigDataFunction serializer) { - this.sourceSerializer = serializer; - return this; - } - - @Override - protected @NotNull SourceMapBuilder getThis() { - return this; - } - - @Override - public @NotNull ConfiguredMap build() { - return new ConfiguredMap<>( - new ValueManifest<>(provider, path, headerComments, inlineComment, defaultValue), - this.supplier, this.keyClass, this.keyParser, - this.valueClass, this.sourceParser.andThen(this.valueParser), - this.keySerializer, this.valueSerializer.andThen(this.sourceSerializer) - ); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/value/ConfigValueBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/value/ConfigValueBuilder.java deleted file mode 100644 index 58f9f35..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/value/ConfigValueBuilder.java +++ /dev/null @@ -1,64 +0,0 @@ -package cc.carm.lib.configuration.core.builder.value; - -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.function.ConfigValueParser; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; - -public class ConfigValueBuilder { - - protected final @NotNull Class valueClass; - - public ConfigValueBuilder(@NotNull Class valueClass) { - this.valueClass = valueClass; - } - - public @NotNull SectionValueBuilder fromSection() { - return fromSection(ConfigValueParser.required(), ConfigDataFunction.required()); - } - - public @NotNull SectionValueBuilder fromSection(@NotNull ConfigValueParser, V> valueParser, - @NotNull ConfigDataFunction> valueSerializer) { - return new SectionValueBuilder<>(this.valueClass, valueParser, valueSerializer); - } - - public @NotNull SourceValueBuilder from(Class sourceClass) { - return from( - sourceClass, ConfigDataFunction.required(), ConfigValueParser.required(), - ConfigDataFunction.required(), ConfigDataFunction.required() - ); - } - - public @NotNull SourceValueBuilder from(@NotNull Class sourceClass, - @NotNull ConfigDataFunction sourceParser, - @NotNull ConfigValueParser valueParser, - @NotNull ConfigDataFunction valueSerializer, - @NotNull ConfigDataFunction sourceSerializer) { - return new SourceValueBuilder<>( - sourceClass, sourceParser, this.valueClass, valueParser, - valueSerializer, sourceSerializer - ); - } - - public @NotNull SourceValueBuilder fromObject() { - return from( - Object.class, ConfigDataFunction.identity(), - ConfigValueParser.castObject(valueClass), - s -> { - if (s instanceof Enum) return ((Enum) s).name(); - else return s; - }, ConfigDataFunction.toObject() - ); - } - - public @NotNull SourceValueBuilder fromString() { - return from( - String.class, ConfigDataFunction.castToString(), - ConfigValueParser.parseString(this.valueClass), - ConfigDataFunction.castToString(), ConfigDataFunction.toObject() - ); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/value/SectionValueBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/value/SectionValueBuilder.java deleted file mode 100644 index ea29928..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/value/SectionValueBuilder.java +++ /dev/null @@ -1,54 +0,0 @@ -package cc.carm.lib.configuration.core.builder.value; - -import cc.carm.lib.configuration.core.builder.CommonConfigBuilder; -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.function.ConfigValueParser; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import cc.carm.lib.configuration.core.value.type.ConfiguredSection; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; - -public class SectionValueBuilder - extends CommonConfigBuilder> { - - - protected final @NotNull Class valueClass; - - protected @NotNull ConfigValueParser, V> parser; - protected @NotNull ConfigDataFunction> serializer; - - public SectionValueBuilder(@NotNull Class valueClass, - @NotNull ConfigValueParser, V> parser, - @NotNull ConfigDataFunction> serializer) { - this.valueClass = valueClass; - this.parser = parser; - this.serializer = serializer; - } - - - @Override - protected @NotNull SectionValueBuilder getThis() { - return this; - } - - public @NotNull SectionValueBuilder parseValue(ConfigDataFunction, V> valueParser) { - return parseValue((section, path) -> valueParser.parse(section)); - } - - public @NotNull SectionValueBuilder parseValue(ConfigValueParser, V> valueParser) { - this.parser = valueParser; - return this; - } - - public @NotNull SectionValueBuilder serializeValue(ConfigDataFunction> serializer) { - this.serializer = serializer; - return this; - } - - @Override - public @NotNull ConfiguredSection build() { - return new ConfiguredSection<>(buildManifest(), this.valueClass, this.parser, this.serializer); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/builder/value/SourceValueBuilder.java b/core/src/main/java/cc/carm/lib/configuration/core/builder/value/SourceValueBuilder.java deleted file mode 100644 index 203042a..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/builder/value/SourceValueBuilder.java +++ /dev/null @@ -1,70 +0,0 @@ -package cc.carm.lib.configuration.core.builder.value; - -import cc.carm.lib.configuration.core.builder.CommonConfigBuilder; -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.function.ConfigValueParser; -import cc.carm.lib.configuration.core.value.type.ConfiguredValue; -import org.jetbrains.annotations.NotNull; - -public class SourceValueBuilder extends CommonConfigBuilder> { - - protected final @NotNull Class sourceClass; - protected @NotNull ConfigDataFunction sourceParser; - - protected final @NotNull Class valueClass; - protected @NotNull ConfigValueParser valueParser; - - protected @NotNull ConfigDataFunction sourceSerializer; - protected @NotNull ConfigDataFunction valueSerializer; - - public SourceValueBuilder(@NotNull Class sourceClass, @NotNull ConfigDataFunction sourceParser, - @NotNull Class valueClass, @NotNull ConfigValueParser valueParser, - @NotNull ConfigDataFunction valueSerializer, - @NotNull ConfigDataFunction sourceSerializer) { - this.sourceClass = sourceClass; - this.sourceParser = sourceParser; - this.valueClass = valueClass; - this.valueParser = valueParser; - this.sourceSerializer = sourceSerializer; - this.valueSerializer = valueSerializer; - } - - @Override - protected @NotNull SourceValueBuilder getThis() { - return this; - } - - public @NotNull SourceValueBuilder parseSource(@NotNull ConfigDataFunction sourceParser) { - this.sourceParser = sourceParser; - return this; - } - - public @NotNull SourceValueBuilder parseValue(ConfigDataFunction valueParser) { - return parseValue((section, path) -> valueParser.parse(section)); - } - - public @NotNull SourceValueBuilder parseValue(@NotNull ConfigValueParser valueParser) { - this.valueParser = valueParser; - return this; - } - - public @NotNull SourceValueBuilder serializeValue(@NotNull ConfigDataFunction serializer) { - this.valueSerializer = serializer; - return this; - } - - public @NotNull SourceValueBuilder serializeSource(@NotNull ConfigDataFunction serializer) { - this.sourceSerializer = serializer; - return this; - } - - @Override - public @NotNull ConfiguredValue build() { - return new ConfiguredValue<>( - buildManifest(), this.valueClass, - this.valueParser.compose(this.sourceParser), - this.valueSerializer.andThen(sourceSerializer) - ); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/function/ConfigValueParser.java b/core/src/main/java/cc/carm/lib/configuration/core/function/ConfigValueParser.java deleted file mode 100644 index 7dc7762..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/function/ConfigValueParser.java +++ /dev/null @@ -1,159 +0,0 @@ -package cc.carm.lib.configuration.core.function; - - -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; -import java.util.UUID; - -@FunctionalInterface -public interface ConfigValueParser { - - @Nullable R parse(@NotNull T data, @Nullable R defaultValue) throws Exception; - - default ConfigValueParser andThen(@NotNull ConfigValueParser after) { - Objects.requireNonNull(after); - return ((data, defaultValue) -> { - R result = parse(data, null); - if (result == null) return defaultValue; - else return after.parse(result, defaultValue); - }); - } - - default ConfigValueParser compose(@NotNull ConfigDataFunction before) { - Objects.requireNonNull(before); - return ((data, defaultValue) -> { - T result = before.parse(data); - return parse(result, defaultValue); - }); - } - - - @Contract(pure = true) - static @NotNull ConfigValueParser identity() { - return (input, defaultValue) -> input; - } - - @Contract(pure = true) - static @NotNull ConfigValueParser toObject() { - return (input, defaultValue) -> input; - } - - @Contract(pure = true) - static @NotNull ConfigValueParser required() { - return (input, defaultValue) -> { - throw new IllegalArgumentException("Please specify the value parser."); - }; - } - - @Contract(pure = true) - static @NotNull ConfigValueParser castObject(Class valueClass) { - if (Number.class.isAssignableFrom(valueClass)) return castNumber(valueClass); - else return (input, defaultValue) -> { - if (Boolean.class.isAssignableFrom(valueClass) || boolean.class.isAssignableFrom(valueClass)) { - input = booleanValue().parse(input, (Boolean) defaultValue); - } else if (Enum.class.isAssignableFrom(valueClass) && input instanceof String) { - String enumName = (String) input; - input = valueClass.getDeclaredMethod("valueOf", String.class).invoke(null, enumName); - } else if (UUID.class.isAssignableFrom(valueClass) && input instanceof String) { - input = UUID.fromString((String) input); - } - - if (valueClass.isInstance(input)) return valueClass.cast(input); - else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName()); - }; - } - - - @Contract(pure = true) - static @NotNull ConfigValueParser castNumber(Class valueClass) { - return (input, defaultValue) -> { - if (Long.class.isAssignableFrom(valueClass) || long.class.isAssignableFrom(valueClass)) { - input = longValue().parse(input, (Long) defaultValue); - } else if (Integer.class.isAssignableFrom(valueClass) || int.class.isAssignableFrom(valueClass)) { - input = intValue().parse(input, (Integer) defaultValue); - } else if (Float.class.isAssignableFrom(valueClass) || float.class.isAssignableFrom(valueClass)) { - input = floatValue().parse(input, (Float) defaultValue); - } else if (Double.class.isAssignableFrom(valueClass) || double.class.isAssignableFrom(valueClass)) { - input = doubleValue().parse(input, (Double) defaultValue); - } else if (Byte.class.isAssignableFrom(valueClass) || byte.class.isAssignableFrom(valueClass)) { - input = byteValue().parse(input, (Byte) defaultValue); - } else if (Short.class.isAssignableFrom(valueClass) || short.class.isAssignableFrom(valueClass)) { - input = shortValue().parse(input, (Short) defaultValue); - } - if (valueClass.isInstance(input)) return valueClass.cast(input); - else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName()); - }; - } - - @Contract(pure = true) - static @NotNull ConfigValueParser parseString(Class valueClass) { - return (input, defaultValue) -> { - if (valueClass.isInstance(input)) return valueClass.cast(input); - else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName()); - }; - } - - @Contract(pure = true) - static @NotNull ConfigValueParser castToString() { - return (input, defaultValue) -> { - if (input instanceof String) return (String) input; - else return input.toString(); - }; - } - - @Contract(pure = true) - static @NotNull ConfigValueParser intValue() { - return (input, defaultValue) -> ConfigDataFunction.intValue().parse(input); - } - - @Contract(pure = true) - static @NotNull ConfigValueParser shortValue() { - return (input, defaultValue) -> ConfigDataFunction.shortValue().parse(input); - } - - @Contract(pure = true) - static @NotNull ConfigValueParser doubleValue() { - return (input, defaultValue) -> ConfigDataFunction.doubleValue().parse(input); - } - - @Contract(pure = true) - static @NotNull ConfigValueParser byteValue() { - return (input, defaultValue) -> ConfigDataFunction.byteValue().parse(input); - } - - @Contract(pure = true) - static @NotNull ConfigValueParser floatValue() { - return (input, defaultValue) -> ConfigDataFunction.floatValue().parse(input); - } - - @Contract(pure = true) - static @NotNull ConfigValueParser longValue() { - return (input, defaultValue) -> ConfigDataFunction.longValue().parse(input); - } - - @Contract(pure = true) - static @NotNull ConfigValueParser booleanValue() { - return (input, defaultValue) -> ConfigDataFunction.booleanValue().parse(input); - } - - @Contract(pure = true) - static @NotNull > ConfigValueParser enumValue(Class enumClass) { - return (input, defaultValue) -> { - if (input instanceof Enum) { - return enumClass.cast(input); - } else if (input instanceof String) { - return Enum.valueOf(enumClass, (String) input); - } else if (input instanceof Number) { - return enumClass.getEnumConstants()[((Number) input).intValue()]; - } else { - throw new IllegalArgumentException("Cannot cast value to " + enumClass.getName()); - } - }; - } - -} - - diff --git a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationComments.java b/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationComments.java deleted file mode 100644 index e6775cb..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationComments.java +++ /dev/null @@ -1,51 +0,0 @@ -package cc.carm.lib.configuration.core.source; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; - -import java.util.*; - -public class ConfigurationComments { - - protected final @NotNull Map> headerComments = new HashMap<>(); - protected final @NotNull Map inlineComments = new HashMap<>(); - - protected @NotNull Map> getHeaderComments() { - return headerComments; - } - - protected @NotNull Map getInlineComments() { - return inlineComments; - } - - public void setHeaderComments(@Nullable String path, @Nullable List comments) { - - if (comments == null) { - getHeaderComments().remove(path); - } else { - getHeaderComments().put(path, comments); - } - } - - - public void setInlineComment(@NotNull String path, @Nullable String comment) { - if (comment == null) { - getInlineComments().remove(path); - } else { - getInlineComments().put(path, comment); - } - } - - @Nullable - @Unmodifiable - public List getHeaderComment(@Nullable String path) { - return Optional.ofNullable(getHeaderComments().get(path)).map(Collections::unmodifiableList).orElse(null); - } - - public @Nullable String getInlineComment(@NotNull String path) { - return getInlineComments().get(path); - } - - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationProvider.java b/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationProvider.java deleted file mode 100644 index b628326..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationProvider.java +++ /dev/null @@ -1,151 +0,0 @@ -package cc.carm.lib.configuration.core.source; - -import cc.carm.lib.configuration.core.ConfigInitializer; -import cc.carm.lib.configuration.core.Configuration; -import cc.carm.lib.configuration.core.Configuration; -import cc.carm.lib.configuration.core.value.ConfigValue; -import cc.carm.lib.configuration.core.value.impl.CachedConfigValue; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; - -import java.util.List; -import java.util.Optional; - -/** - * 配置文件提供者,用于为 {@link ConfigValue} 提供配置文件的源,以便实现读取、保存等操作。 - * - * @param 配置文件的原生功能类 - */ -public abstract class ConfigurationProvider> { - - protected long updateTime; - - protected ConfigurationProvider() { - this.updateTime = System.currentTimeMillis(); - } - - /** - * 得到配置文件的更新(最后加载)时间。 - * - * @return 更新时间 - */ - public long getUpdateTime() { - return updateTime; - } - - /** - * 用于 {@link CachedConfigValue} 判断缓存值是否过期(即缓存的时间早于配置文件的最后加载时间)。 - * - * @param time 缓存值时的时间戳 - * @return 缓存值是否过期 - */ - public boolean isExpired(long time) { - return getUpdateTime() > time; - } - - /** - * 得到配置文件的原生功能类。 - * - * @return 原生类 - */ - public abstract @NotNull W getConfiguration(); - - /** - * 重载当前配置文件。(将不会保存已修改的内容) - * - * @throws Exception 当重载出现错误时抛出 - */ - public void reload() throws Exception { - onReload(); // 调用重写的Reload方法 - this.updateTime = System.currentTimeMillis(); - } - - /** - * 将当前对配置文件的更改进行保存。 - * - * @throws Exception 当保存出现错误时抛出 - */ - public abstract void save() throws Exception; - - /** - * 针对于不同配置文件类型所执行的重载操作。 - * - * @throws Exception 当操作出现错误时抛出。 - */ - protected abstract void onReload() throws Exception; - - public abstract @Nullable ConfigurationComments getComments(); - - public void setHeaderComment(@Nullable String path, @Nullable List comments) { - if (getComments() == null) return; - getComments().setHeaderComments(path, comments); - } - - public void setInlineComment(@NotNull String path, @Nullable String comment) { - if (getComments() == null) return; - getComments().setInlineComment(path, comment); - } - - @Nullable - @Unmodifiable - public List getHeaderComment(@Nullable String path) { - return Optional.ofNullable(getComments()).map(c -> c.getHeaderComment(path)).orElse(null); - } - - public @Nullable String getInlineComment(@NotNull String path) { - return Optional.ofNullable(getComments()).map(c -> c.getInlineComment(path)).orElse(null); - } - - public abstract @NotNull ConfigInitializer> getInitializer(); - - /** - * 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。 - * - * @param configClazz 配置文件类,须继承于 {@link Configuration} 。 - */ - public void initialize(Class configClazz) { - initialize(configClazz, true); - } - - /** - * 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。 - * - * @param configClazz 配置文件类,须继承于 {@link Configuration} 。 - * @param saveDefaults 是否写入默认值(默认为 true)。 - */ - public void initialize(Class configClazz, boolean saveDefaults) { - this.getInitializer().initialize(configClazz, saveDefaults); - } - - /** - * 初始化指定类的所有 {@link ConfigValue} 对象。 - * - * @param configClazz 配置文件类,须继承于 {@link Configuration} 。 - * @param saveDefaults 是否写入默认值(默认为 true)。 - * @param loadSubClasses 是否加载内部子类(默认为 true)。 - */ - public void initialize(Class configClazz, boolean saveDefaults, boolean loadSubClasses) { - this.getInitializer().initialize(configClazz, saveDefaults, loadSubClasses); - } - - /** - * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。 - * - * @param config 配置文件实例类,须实现 {@link Configuration} 。 - */ - public void initialize(@NotNull Configuration config) { - this.getInitializer().initialize(config, true); - } - - /** - * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。 - * - * @param config 配置文件实例类,须实现 {@link Configuration} 。 - * @param saveDefaults 是否写入默认值(默认为 true)。 - */ - public void initialize(@NotNull Configuration config, boolean saveDefaults) { - this.getInitializer().initialize(config, saveDefaults); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationReader.java b/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationReader.java deleted file mode 100644 index 96b85f4..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationReader.java +++ /dev/null @@ -1,194 +0,0 @@ -package cc.carm.lib.configuration.core.source; - -import cc.carm.lib.configuration.core.function.ConfigValueParser; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -interface ConfigurationReader { - - ConfigurationWrapper getWrapper(); - - default boolean isBoolean(@NotNull String path) { - return getWrapper().isType(path, Boolean.class); - } - - default boolean getBoolean(@NotNull String path) { - return getBoolean(path, false); - } - - @Contract("_, !null -> !null") - default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) { - return getWrapper().get(path, def, ConfigValueParser.booleanValue()); - } - - default @Nullable Boolean isByte(@NotNull String path) { - return getWrapper().isType(path, Byte.class); - } - - default @Nullable Byte getByte(@NotNull String path) { - return getByte(path, (byte) 0); - } - - @Contract("_, !null -> !null") - default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) { - return getWrapper().get(path, def, ConfigValueParser.byteValue()); - } - - default boolean isShort(@NotNull String path) { - return getWrapper().isType(path, Short.class); - } - - default @Nullable Short getShort(@NotNull String path) { - return getShort(path, (short) 0); - } - - @Contract("_, !null -> !null") - default @Nullable Short getShort(@NotNull String path, @Nullable Short def) { - return getWrapper().get(path, def, ConfigValueParser.shortValue()); - } - - - default boolean isInt(@NotNull String path) { - return getWrapper().isType(path, Integer.class); - } - - default @Nullable Integer getInt(@NotNull String path) { - return getInt(path, 0); - } - - @Contract("_, !null -> !null") - default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) { - return getWrapper().get(path, def, ConfigValueParser.intValue()); - } - - - default boolean isLong(@NotNull String path) { - return getWrapper().isType(path, Long.class); - } - - default @Nullable Long getLong(@NotNull String path) { - return getLong(path, 0L); - } - - @Contract("_, !null -> !null") - default @Nullable Long getLong(@NotNull String path, @Nullable Long def) { - return getWrapper().get(path, def, ConfigValueParser.longValue()); - } - - - default boolean isFloat(@NotNull String path) { - return getWrapper().isType(path, Float.class); - } - - default @Nullable Float getFloat(@NotNull String path) { - return getFloat(path, 0.0F); - } - - @Contract("_, !null -> !null") - default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) { - return getWrapper().get(path, def, ConfigValueParser.floatValue()); - } - - - default boolean isDouble(@NotNull String path) { - return getWrapper().isType(path, Double.class); - } - - default @Nullable Double getDouble(@NotNull String path) { - return getDouble(path, 0.0D); - } - - @Contract("_, !null -> !null") - default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) { - return getWrapper().get(path, def, ConfigValueParser.doubleValue()); - } - - - default boolean isChar(@NotNull String path) { - return getWrapper().isType(path, Boolean.class); - } - - default @Nullable Character getChar(@NotNull String path) { - return getChar(path, null); - } - - @Contract("_, !null -> !null") - default @Nullable Character getChar(@NotNull String path, @Nullable Character def) { - return getWrapper().get(path, def, Character.class); - } - - - default boolean isString(@NotNull String path) { - return getWrapper().isType(path, String.class); - } - - default @Nullable String getString(@NotNull String path) { - return getString(path, null); - } - - @Contract("_, !null -> !null") - default @Nullable String getString(@NotNull String path, @Nullable String def) { - return getWrapper().get(path, def, String.class); - } - - default @NotNull List getList(@NotNull String path, @NotNull ConfigValueParser parser) { - return parseList(getWrapper().getList(path), parser); - } - - @Unmodifiable - default @NotNull List getStringList(@NotNull String path) { - return getList(path, ConfigValueParser.castToString()); - } - - @Unmodifiable - default @NotNull List getIntegerList(@NotNull String path) { - return getList(path, ConfigValueParser.intValue()); - } - - @Unmodifiable - default @NotNull List getLongList(@NotNull String path) { - return getList(path, ConfigValueParser.longValue()); - } - - @Unmodifiable - default @NotNull List getDoubleList(@NotNull String path) { - return getList(path, ConfigValueParser.doubleValue()); - } - - @Unmodifiable - default @NotNull List getFloatList(@NotNull String path) { - return getList(path, ConfigValueParser.floatValue()); - } - - @Unmodifiable - default @NotNull List getByteList(@NotNull String path) { - return getList(path, ConfigValueParser.byteValue()); - } - - @Unmodifiable - default @NotNull List getCharList(@NotNull String path) { - return getList(path, ConfigValueParser.castObject(Character.class)); - } - - @Unmodifiable - static @NotNull List parseList(@Nullable List list, ConfigValueParser parser) { - if (list == null) return Collections.emptyList(); - List values = new ArrayList<>(); - for (Object o : list) { - try { - T parsed = parser.parse(o, null); - if (parsed != null) values.add(parsed); - } catch (Exception ignored) { - } - } - return values; - } - - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationWrapper.java b/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationWrapper.java deleted file mode 100644 index 478af95..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationWrapper.java +++ /dev/null @@ -1,73 +0,0 @@ -package cc.carm.lib.configuration.core.source; - -import cc.carm.lib.configuration.core.function.ConfigValueParser; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -public interface ConfigurationWrapper extends ConfigurationReader { - - @Override - default ConfigurationWrapper getWrapper() { - return this; - } - - @NotNull S getSource(); - - @NotNull - Set getKeys(boolean deep); - - @NotNull - Map getValues(boolean deep); - - void set(@NotNull String path, @Nullable Object value); - - boolean contains(@NotNull String path); - - default boolean isType(@NotNull String path, @NotNull Class typeClass) { - return typeClass.isInstance(get(path)); - } - - @Nullable Object get(@NotNull String path); - - default @Nullable T get(@NotNull String path, @NotNull Class clazz) { - return get(path, null, clazz); - } - - default @Nullable T get(@NotNull String path, @NotNull ConfigValueParser parser) { - return get(path, null, parser); - } - - @Contract("_,!null,_->!null") - default @Nullable T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class clazz) { - return get(path, defaultValue, ConfigValueParser.castObject(clazz)); - } - - @Contract("_,!null,_->!null") - default @Nullable T get(@NotNull String path, @Nullable T defaultValue, - @NotNull ConfigValueParser parser) { - Object value = get(path); - if (value != null) { - try { - return parser.parse(value, defaultValue); - } catch (Exception e) { - e.printStackTrace(); - } - } - return defaultValue; - } - - boolean isList(@NotNull String path); - - @Nullable List getList(@NotNull String path); - - boolean isConfigurationSection(@NotNull String path); - - @Nullable - ConfigurationWrapper getConfigurationSection(@NotNull String path); - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/source/impl/FileConfigProvider.java b/core/src/main/java/cc/carm/lib/configuration/core/source/impl/FileConfigProvider.java deleted file mode 100644 index ffffd13..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/source/impl/FileConfigProvider.java +++ /dev/null @@ -1,89 +0,0 @@ -package cc.carm.lib.configuration.core.source.impl; - -import cc.carm.lib.configuration.core.source.ConfigurationProvider; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URL; -import java.net.URLConnection; -import java.nio.file.Files; -import java.util.Objects; - -public abstract class FileConfigProvider> extends ConfigurationProvider { - - protected final @NotNull File file; - - protected FileConfigProvider(@NotNull File file) { - this.file = file; - } - - public @NotNull File getFile() { - return file; - } - - public void initializeFile(@Nullable String sourcePath) throws IOException { - if (this.file.exists()) return; - - File parent = this.file.getParentFile(); - if (parent != null && !parent.exists() && !parent.mkdirs()) { - throw new IOException("Failed to create directory " + file.getParentFile().getAbsolutePath()); - } - - if (!this.file.createNewFile()) { - throw new IOException("Failed to create file " + file.getAbsolutePath()); - } - - if (sourcePath != null) { - try { - saveResource(sourcePath, true); - } catch (IllegalArgumentException ignored) { - } - } - } - - public void saveResource(@NotNull String resourcePath, boolean replace) - throws IOException, IllegalArgumentException { - Objects.requireNonNull(resourcePath, "ResourcePath cannot be null"); - if (resourcePath.isEmpty()) throw new IllegalArgumentException("ResourcePath cannot be empty"); - - resourcePath = resourcePath.replace('\\', '/'); - - URL url = this.getClass().getClassLoader().getResource(resourcePath); - if (url == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists"); - - File outDir = file.getParentFile(); - - if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir); - if (!file.exists() || replace) { - try (OutputStream out = Files.newOutputStream(file.toPath())) { - URLConnection connection = url.openConnection(); - connection.setUseCaches(false); - try (InputStream in = connection.getInputStream()) { - byte[] buf = new byte[1024]; - int len; - while ((len = in.read(buf)) > 0) { - out.write(buf, 0, len); - } - } - } - } - } - - @Nullable - public InputStream getResource(@NotNull String filename) { - try { - URL url = this.getClass().getClassLoader().getResource(filename); - if (url == null) return null; - URLConnection connection = url.openConnection(); - connection.setUseCaches(false); - return connection.getInputStream(); - } catch (IOException ex) { - return null; - } - } -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/util/MapFactory.java b/core/src/main/java/cc/carm/lib/configuration/core/util/MapFactory.java deleted file mode 100644 index 7fe98cc..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/util/MapFactory.java +++ /dev/null @@ -1,67 +0,0 @@ -package cc.carm.lib.configuration.core.util; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.TreeMap; - -public class MapFactory, K, V> { - - private final S map; - - protected MapFactory(S map) { - this.map = map; - } - - public MapFactory put(K key, V value) { - this.map.put(key, value); - return this; - } - - public MapFactory remove(K key) { - this.map.remove(key); - return this; - } - - public MapFactory clear() { - this.map.clear(); - return this; - } - - public S build() { - return get(); - } - - public S get() { - return map; - } - - public static MapFactory, K, V> hashMap() { - return new MapFactory<>(new HashMap<>()); - } - - public static MapFactory, K, V> hashMap(K firstKey, V firstValue) { - return MapFactory.hashMap().put(firstKey, firstValue); - } - - public static MapFactory, K, V> linkedMap() { - return of(new LinkedHashMap<>()); - } - - public static MapFactory, K, V> linkedMap(K firstKey, V firstValue) { - return MapFactory.linkedMap().put(firstKey, firstValue); - } - - public static , V> MapFactory, K, V> treeMap() { - return of(new TreeMap<>()); - } - - public static , V> MapFactory, K, V> treeMap(K firstKey, V firstValue) { - return MapFactory.treeMap().put(firstKey, firstValue); - } - - public static , K, V> MapFactory of(M map) { - return new MapFactory<>(map); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/ConfigValue.java b/core/src/main/java/cc/carm/lib/configuration/core/value/ConfigValue.java deleted file mode 100644 index f8028ca..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/ConfigValue.java +++ /dev/null @@ -1,110 +0,0 @@ -package cc.carm.lib.configuration.core.value; - -import cc.carm.lib.configuration.core.builder.ConfigBuilder; -import cc.carm.lib.configuration.core.source.ConfigurationProvider; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -public abstract class ConfigValue extends ValueManifest { - - public static @NotNull ConfigBuilder builder() { - return new ConfigBuilder(); - } - - protected ConfigValue(@NotNull ValueManifest manifest) { - super(manifest.provider, manifest.configPath, manifest.headerComments, manifest.inlineComment, manifest.defaultValue); - } - - /** - * @param provider Provider of config files {@link ConfigurationProvider} - * @param configPath Config path of this value - * @param headerComments Header comment contents - * @param inlineComments Inline comment contents - * @param defaultValue The default value - * @deprecated Please use {@link #ConfigValue(ValueManifest)} instead. - */ - @Deprecated - protected ConfigValue(@Nullable ConfigurationProvider provider, @Nullable String configPath, - @Nullable List headerComments, @Nullable String inlineComments, - @Nullable T defaultValue) { - super(provider, configPath, headerComments, inlineComments, defaultValue); - } - - public void initialize(@NotNull ConfigurationProvider provider, boolean saveDefault, @NotNull String configPath, - @Nullable List headerComments, @Nullable String inlineComments) { - this.initialize(provider, configPath, headerComments, inlineComments); - if (saveDefault) setDefault(); - } - - /** - * 得到该配置的设定值(即读取到的值)。 - *
若初始化时未写入默认值,则可以通过 {@link #getOrDefault()} 方法在该设定值为空时获取默认值。 - * - * @return 设定值 - */ - public abstract @Nullable T get(); - - /** - * 得到该配置的设定值,若不存在,则返回默认值。 - * - * @return 设定值或默认值 - */ - public @Nullable T getOrDefault() { - return getOptional().orElse(getDefaultValue()); - } - - /** - * 得到该配置的非空值。 - * - * @return 非空值 - * @throws NullPointerException 对应数据为空时抛出 - */ - public @NotNull T getNotNull() { - return Objects.requireNonNull(getOrDefault(), "Value(" + configPath + ") is null."); - } - - public @NotNull Optional<@Nullable T> getOptional() { - return Optional.ofNullable(get()); - } - - /** - * 设定该配置的值。 - *
设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationProvider#save()} 方法。 - * - * @param value 配置的值 - */ - public abstract void set(@Nullable T value); - - /** - * 初始化该配置的默认值。 - *
设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationProvider#save()} 方法。 - */ - public void setDefault() { - setDefault(false); - } - - /** - * 将该配置的值设置为默认值。 - *
设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationProvider#save()} 方法。 - * - * @param override 是否覆盖已设定的值 - */ - public void setDefault(boolean override) { - if (!override && getConfiguration().contains(getConfigPath())) return; - Optional.ofNullable(getDefaultValue()).ifPresent(this::set); - } - - /** - * 判断加载的配置是否与默认值相同。 - * - * @return 获取当前值是否为默认值。 - */ - public boolean isDefault() { - return Objects.equals(getDefaultValue(), get()); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/ValueManifest.java b/core/src/main/java/cc/carm/lib/configuration/core/value/ValueManifest.java deleted file mode 100644 index cde0cc2..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/ValueManifest.java +++ /dev/null @@ -1,126 +0,0 @@ -package cc.carm.lib.configuration.core.value; - -import cc.carm.lib.configuration.core.ConfigInitializer; -import cc.carm.lib.configuration.core.source.ConfigurationProvider; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; - -import java.util.List; -import java.util.Optional; - -/** - * ConfigValue Manifests. - * The basic information that describes a configuration value. - * - * @param Value type - * @author CarmJos - */ -public class ValueManifest { - - public static ValueManifest of(@Nullable ConfigurationProvider provider, @Nullable String configPath, - @Nullable List headerComments, @Nullable String inlineComments) { - return new ValueManifest<>(provider, configPath, headerComments, inlineComments, null); - } - - public static ValueManifest of(@Nullable ConfigurationProvider provider, @Nullable String configPath, - @Nullable List headerComments, @Nullable String inlineComments, - @Nullable V defaultValue) { - return new ValueManifest<>(provider, configPath, headerComments, inlineComments, defaultValue); - } - - protected @Nullable ConfigurationProvider provider; - protected @Nullable String configPath; - - protected @Nullable List headerComments; - protected @Nullable String inlineComment; - - protected @Nullable T defaultValue; - - /** - * @param provider Provider of config files {@link ConfigurationProvider} - * @param configPath Config path of this value - * @param headerComments Header comment contents - * @param inlineComment Inline comment content - * @param defaultValue The default value - */ - public ValueManifest(@Nullable ConfigurationProvider provider, @Nullable String configPath, - @Nullable List headerComments, @Nullable String inlineComment, - @Nullable T defaultValue) { - this.provider = provider; - this.configPath = configPath; - this.headerComments = headerComments; - this.inlineComment = inlineComment; - this.defaultValue = defaultValue; - } - - /** - * The initialize method for {@link ConfigInitializer}, which is used to initialize the value. - * - * @param provider Provider of config files {@link ConfigurationProvider} - * @param configPath Config path of this value - * @param headerComments Header comment contents - * @param inlineComment Inline comment content - */ - protected void initialize(@NotNull ConfigurationProvider provider, @NotNull String configPath, - @Nullable List headerComments, @Nullable String inlineComment) { - if (this.provider == null) this.provider = provider; - if (this.configPath == null) this.configPath = configPath; - if (this.headerComments == null) this.headerComments = headerComments; - if (this.inlineComment == null) this.inlineComment = inlineComment; - - if (getHeaderComments() != null) { - this.provider.setHeaderComment(getConfigPath(), getHeaderComments()); - } - if (getInlineComment() != null) { - this.provider.setInlineComment(getConfigPath(), getInlineComment()); - } - } - - public @Nullable T getDefaultValue() { - return this.defaultValue; - } - - public void setDefaultValue(@Nullable T defaultValue) { - this.defaultValue = defaultValue; - } - - public @NotNull ConfigurationProvider getProvider() { - return Optional.ofNullable(this.provider) - .orElseThrow(() -> new IllegalStateException("Value(" + configPath + ") does not have a provider.")); - } - - public final @NotNull ConfigurationWrapper getConfiguration() { - try { - return getProvider().getConfiguration(); - } catch (Exception ex) { - throw new IllegalStateException("Value(" + configPath + ") has not been initialized", ex); - } - } - - public @NotNull String getConfigPath() { - return Optional.ofNullable(this.configPath) - .orElseThrow(() -> new IllegalStateException("No section path provided.")); - } - - protected Object getValue() { - String path = getConfigPath(); // 当未指定路径时,优先抛出异常 - return getConfiguration().get(path); - } - - protected void setValue(@Nullable Object value) { - getConfiguration().set(getConfigPath(), value); - } - - public @Nullable String getInlineComment() { - return inlineComment; - } - - @Unmodifiable - public @Nullable List getHeaderComments() { - return headerComments; - } - - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/impl/CachedConfigValue.java b/core/src/main/java/cc/carm/lib/configuration/core/value/impl/CachedConfigValue.java deleted file mode 100644 index c5fcf0d..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/impl/CachedConfigValue.java +++ /dev/null @@ -1,48 +0,0 @@ -package cc.carm.lib.configuration.core.value.impl; - -import cc.carm.lib.configuration.core.value.ConfigValue; -import cc.carm.lib.configuration.core.value.ValueManifest; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public abstract class CachedConfigValue extends ConfigValue { - - - protected @Nullable T cachedValue; - protected long parsedTime = -1; - - protected CachedConfigValue(@NotNull ValueManifest manifest) { - super(manifest); - } - - protected T updateCache(T value) { - this.parsedTime = System.currentTimeMillis(); - this.cachedValue = value; - return getCachedValue(); - } - - public @Nullable T getCachedValue() { - return cachedValue; - } - - public boolean isExpired() { - return this.parsedTime <= 0 || getProvider().isExpired(this.parsedTime); - } - - protected final T getDefaultFirst(@Nullable T value) { - return updateCache(this.defaultValue == null ? value : this.defaultValue); - } - - protected @Nullable T getCachedOrDefault() { - return getCachedOrDefault(null); - } - - @Contract("!null->!null") - protected T getCachedOrDefault(@Nullable T emptyValue) { - if (getCachedValue() != null) return getCachedValue(); - else if (getDefaultValue() != null) return getDefaultValue(); - else return emptyValue; - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/impl/ConfigValueMap.java b/core/src/main/java/cc/carm/lib/configuration/core/value/impl/ConfigValueMap.java deleted file mode 100644 index b68304e..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/impl/ConfigValueMap.java +++ /dev/null @@ -1,214 +0,0 @@ -package cc.carm.lib.configuration.core.value.impl; - -import cc.carm.lib.configuration.core.builder.map.ConfigMapCreator; -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import cc.carm.lib.configuration.core.value.ValueManifest; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; - -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Supplier; - -public abstract class ConfigValueMap extends CachedConfigValue> implements Map { - - public static @NotNull ConfigMapCreator builderOf(@NotNull Class keyClass, - @NotNull Class valueClass) { - return builder().asMap(keyClass, valueClass); - } - - protected final @NotNull Supplier> supplier; - - protected final @NotNull Class sourceClass; - protected final @NotNull Class keyClass; - protected final @NotNull Class valueClass; - - protected final @NotNull ConfigDataFunction keyParser; - protected final @NotNull ConfigDataFunction valueParser; - - protected final @NotNull ConfigDataFunction keySerializer; - protected final @NotNull ConfigDataFunction valueSerializer; - - - protected ConfigValueMap(@NotNull ValueManifest> manifest, @NotNull Class sourceClass, - @NotNull Supplier> mapObjSupplier, - @NotNull Class keyClass, @NotNull ConfigDataFunction keyParser, - @NotNull Class valueClass, @NotNull ConfigDataFunction valueParser, - @NotNull ConfigDataFunction keySerializer, - @NotNull ConfigDataFunction valueSerializer) { - super(manifest); - this.supplier = mapObjSupplier; - this.sourceClass = sourceClass; - this.keyClass = keyClass; - this.valueClass = valueClass; - this.keyParser = keyParser; - this.valueParser = valueParser; - this.keySerializer = keySerializer; - this.valueSerializer = valueSerializer; - } - - public @NotNull Class getSourceClass() { - return sourceClass; - } - - public @NotNull Class getKeyClass() { - return keyClass; - } - - public @NotNull Class getValueClass() { - return valueClass; - } - - public @NotNull ConfigDataFunction getKeyParser() { - return keyParser; - } - - public @NotNull ConfigDataFunction getValueParser() { - return valueParser; - } - - public @NotNull ConfigDataFunction getKeySerializer() { - return keySerializer; - } - - public @NotNull ConfigDataFunction getValueSerializer() { - return valueSerializer; - } - - public abstract S getSource(ConfigurationWrapper section, String dataKey); - - @Override - public @NotNull Map get() { - if (!isExpired()) return getCachedOrDefault(supplier.get()); - - // 已过时的数据,需要重新解析一次。 - Map map = supplier.get(); - - ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath()); - if (section == null) return getDefaultFirst(map); - - Set keys = section.getKeys(false); - if (keys.isEmpty()) return getDefaultFirst(map); - - for (String dataKey : keys) { - S dataVal = getSource(section, dataKey); - if (dataVal == null) continue; - try { - K key = keyParser.parse(dataKey); - V value = valueParser.parse(dataVal); - map.put(key, value); - } catch (Exception e) { - e.printStackTrace(); - } - } - - return updateCache(map); - } - - @Override - public V get(Object key) { - return get().get(key); - } - - public V getNotNull(Object key) { - return Objects.requireNonNull(get(key)); - } - - @Override - public void set(@Nullable Map value) { - updateCache(value); - if (value == null) setValue(null); - else { - Map data = new LinkedHashMap<>(); - for (Map.Entry entry : value.entrySet()) { - try { - data.put( - keySerializer.parse(entry.getKey()), - valueSerializer.parse(entry.getValue()) - ); - } catch (Exception e) { - e.printStackTrace(); - } - } - setValue(data); - } - } - - public @NotNull T modifyValue(Function, T> function) { - Map m = get(); - T result = function.apply(m); - set(m); - return result; - } - - public @NotNull Map modifyMap(Consumer> consumer) { - Map m = get(); - consumer.accept(m); - set(m); - return m; - } - - @Override - public int size() { - return get().size(); - } - - @Override - public boolean isEmpty() { - return get().isEmpty(); - } - - @Override - public boolean containsKey(Object key) { - return get().containsKey(key); - } - - @Override - public boolean containsValue(Object value) { - return get().containsValue(value); - } - - @Nullable - @Override - public V put(K key, V value) { - return modifyValue(m -> m.put(key, value)); - } - - @Override - public V remove(Object key) { - return modifyValue(m -> m.remove(key)); - } - - @Override - public void putAll(@NotNull Map m) { - modifyMap(map -> map.putAll(m)); - } - - @Override - public void clear() { - modifyMap(Map::clear); - } - - @NotNull - @Override - public Set keySet() { - return get().keySet(); - } - - @NotNull - @Override - public Collection values() { - return get().values(); - } - - @NotNull - @Override - @Unmodifiable - public Set> entrySet() { - return get().entrySet(); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredList.java b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredList.java deleted file mode 100644 index 567be17..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredList.java +++ /dev/null @@ -1,229 +0,0 @@ -package cc.carm.lib.configuration.core.value.type; - -import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder; -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.value.ValueManifest; -import cc.carm.lib.configuration.core.value.impl.CachedConfigValue; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.function.Consumer; -import java.util.function.Function; - -public class ConfiguredList extends CachedConfigValue> implements List { - - public static @NotNull ConfigListBuilder builderOf(@NotNull Class valueClass) { - return builder().asList(valueClass); - } - - public static @NotNull ConfiguredList of(@NotNull Class valueClass, @NotNull Collection defaults) { - return builderOf(valueClass).fromObject().defaults(defaults).build(); - } - - @SafeVarargs - public static @NotNull ConfiguredList of(@NotNull Class valueClass, @NotNull V... defaults) { - return builderOf(valueClass).fromObject().defaults(defaults).build(); - } - - @SafeVarargs - @SuppressWarnings("unchecked") - public static @NotNull ConfiguredList of(@NotNull V defaultValue, @NotNull V... moreDefaults) { - Collection values = new ArrayList<>(); - values.add(defaultValue); - values.addAll(Arrays.asList(moreDefaults)); - return of((Class) defaultValue.getClass(), values); - } - - protected final @NotNull Class valueClass; - - protected final @NotNull ConfigDataFunction parser; - protected final @NotNull ConfigDataFunction serializer; - - public ConfiguredList(@NotNull ValueManifest> manifest, @NotNull Class valueClass, - @NotNull ConfigDataFunction parser, - @NotNull ConfigDataFunction serializer) { - super(manifest); - this.valueClass = valueClass; - this.parser = parser; - this.serializer = serializer; - } - - @Override - public @NotNull List get() { - if (!isExpired()) return getCachedOrDefault(new ArrayList<>()); - // Data that is outdated and needs to be parsed again. - List list = new ArrayList<>(); - List data = getConfiguration().contains(getConfigPath()) ? - getConfiguration().getList(getConfigPath()) : null; - if (data == null) return getDefaultFirst(list); - for (Object dataVal : data) { - if (dataVal == null) continue; - try { - list.add(parser.parse(dataVal)); - } catch (Exception e) { - e.printStackTrace(); - } - } - return updateCache(list); - } - - @Override - public V get(int index) { - return get().get(index); - } - - public @NotNull List copy() { - return new ArrayList<>(get()); - } - - public @NotNull T handle(Function, T> function) { - List list = get(); - T result = function.apply(list); - set(list); - return result; - } - - public @NotNull List modify(Consumer> consumer) { - List list = get(); - consumer.accept(list); - set(list); - return list; - } - - @Override - public void set(@Nullable List value) { - updateCache(value); - if (value == null) setValue(null); - else { - List data = new ArrayList<>(); - for (V val : value) { - if (val == null) continue; - try { - data.add(serializer.parse(val)); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - setValue(data); - } - } - - @Override - public V set(int index, V element) { - return handle(list -> list.set(index, element)); - } - - @Override - public int size() { - return get().size(); - } - - @Override - public boolean isEmpty() { - return get().isEmpty(); - } - - @Override - public boolean contains(Object o) { - return get().contains(o); - } - - @NotNull - @Override - public Iterator iterator() { - return get().iterator(); - } - - @NotNull - @Override - public Object @NotNull [] toArray() { - return get().toArray(); - } - - @NotNull - @Override - public T @NotNull [] toArray(@NotNull T[] a) { - return get().toArray(a); - } - - @Override - public boolean containsAll(@NotNull Collection c) { - return new HashSet<>(get()).containsAll(c); - } - - @Override - public boolean add(V v) { - handle(list -> list.add(v)); - return true; - } - - @Override - public void add(int index, V element) { - modify(list -> list.add(index, element)); - } - - @Override - public boolean addAll(@NotNull Collection c) { - return handle(list -> list.addAll(c)); - } - - @Override - public boolean addAll(int index, @NotNull Collection c) { - return handle(list -> list.addAll(index, c)); - } - - @Override - public boolean remove(Object o) { - return handle(list -> list.remove(o)); - } - - @Override - public V remove(int index) { - return handle(list -> list.remove(index)); - } - - @Override - public boolean removeAll(@NotNull Collection c) { - return handle(list -> list.removeAll(c)); - } - - @Override - public boolean retainAll(@NotNull Collection c) { - return handle(list -> list.retainAll(c)); - } - - @Override - public void clear() { - modify(List::clear); - } - - @Override - public int indexOf(Object o) { - return get().indexOf(o); - } - - @Override - public int lastIndexOf(Object o) { - return get().lastIndexOf(o); - } - - @NotNull - @Override - public ListIterator listIterator() { - return get().listIterator(); - } - - @NotNull - @Override - public ListIterator listIterator(int index) { - return get().listIterator(index); - } - - @NotNull - @Override - public List subList(int fromIndex, int toIndex) { - return get().subList(fromIndex, toIndex); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredMap.java b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredMap.java deleted file mode 100644 index 75fe74b..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredMap.java +++ /dev/null @@ -1,28 +0,0 @@ -package cc.carm.lib.configuration.core.value.type; - -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import cc.carm.lib.configuration.core.value.ValueManifest; -import cc.carm.lib.configuration.core.value.impl.ConfigValueMap; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; -import java.util.function.Supplier; - -public class ConfiguredMap extends ConfigValueMap { - - public ConfiguredMap(@NotNull ValueManifest> manifest, - @NotNull Supplier> mapObjSupplier, - @NotNull Class keyClass, @NotNull ConfigDataFunction keyParser, - @NotNull Class valueClass, @NotNull ConfigDataFunction valueParser, - @NotNull ConfigDataFunction keySerializer, - @NotNull ConfigDataFunction valueSerializer) { - super(manifest, Object.class, mapObjSupplier, keyClass, keyParser, valueClass, valueParser, keySerializer, valueSerializer); - } - - @Override - public Object getSource(ConfigurationWrapper section, String dataKey) { - return section.get(dataKey); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSection.java b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSection.java deleted file mode 100644 index 76d807e..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSection.java +++ /dev/null @@ -1,97 +0,0 @@ -package cc.carm.lib.configuration.core.value.type; - -import cc.carm.lib.configuration.core.builder.value.SectionValueBuilder; -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.function.ConfigValueParser; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import cc.carm.lib.configuration.core.value.ValueManifest; -import cc.carm.lib.configuration.core.value.impl.CachedConfigValue; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Map; - -public class ConfiguredSection extends CachedConfigValue { - - public static @NotNull SectionValueBuilder builderOf(@NotNull Class valueClass) { - return builder().asValue(valueClass).fromSection(); - } - - protected final @NotNull Class valueClass; - - protected final @NotNull ConfigValueParser, V> parser; - protected final @NotNull ConfigDataFunction> serializer; - - public ConfiguredSection(@NotNull ValueManifest manifest, @NotNull Class valueClass, - @NotNull ConfigValueParser, V> parser, - @NotNull ConfigDataFunction> serializer) { - super(manifest); - this.valueClass = valueClass; - this.parser = parser; - this.serializer = serializer; - } - - /** - * @return Value's type class - */ - public @NotNull Class getValueClass() { - return valueClass; - } - - /** - * @return Value's parser, cast value from section. - */ - public @NotNull ConfigValueParser, V> getParser() { - return parser; - } - - /** - * @return Value's serializer, serialize value to section. - */ - public @NotNull ConfigDataFunction> getSerializer() { - return serializer; - } - - /** - * @return Get the value that parsed from the configuration section. - */ - @Override - public @Nullable V get() { - if (!isExpired()) return getCachedOrDefault(); - // Data that is outdated and needs to be parsed again. - - ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath()); - if (section == null) return getDefaultValue(); - - try { - // If there are no errors, update the cache and return. - return updateCache(this.parser.parse(section, this.defaultValue)); - } catch (Exception e) { - // There was a parsing error, prompted and returned the default value. - e.printStackTrace(); - return getDefaultValue(); - } - - } - - /** - * Use the specified value to update the configuration section. - * Will use {@link #getSerializer()} to serialize the value to section. - * - * @param value The value that needs to be set in the configuration. - */ - @Override - public void set(V value) { - updateCache(value); - if (value == null) setValue(null); - else { - try { - setValue(serializer.parse(value)); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSectionMap.java b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSectionMap.java deleted file mode 100644 index 9d120e6..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSectionMap.java +++ /dev/null @@ -1,32 +0,0 @@ -package cc.carm.lib.configuration.core.value.type; - -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import cc.carm.lib.configuration.core.value.ValueManifest; -import cc.carm.lib.configuration.core.value.impl.ConfigValueMap; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; -import java.util.function.Supplier; - -public class ConfiguredSectionMap extends ConfigValueMap> { - - public ConfiguredSectionMap(@NotNull ValueManifest> manifest, - @NotNull Supplier> mapObjSupplier, - @NotNull Class keyClass, @NotNull ConfigDataFunction keyParser, - @NotNull Class valueClass, @NotNull ConfigDataFunction, V> valueParser, - @NotNull ConfigDataFunction keySerializer, - @NotNull ConfigDataFunction> valueSerializer) { - super( - manifest, ConfigurationWrapper.class, mapObjSupplier, - keyClass, keyParser, valueClass, valueParser, - keySerializer, valueSerializer.andThen(s -> (Object) s) - ); - } - - @Override - public ConfigurationWrapper getSource(ConfigurationWrapper section, String dataKey) { - return section.getConfigurationSection(dataKey); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredValue.java b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredValue.java deleted file mode 100644 index 6a95b39..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredValue.java +++ /dev/null @@ -1,103 +0,0 @@ -package cc.carm.lib.configuration.core.value.type; - -import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder; -import cc.carm.lib.configuration.core.function.ConfigDataFunction; -import cc.carm.lib.configuration.core.function.ConfigValueParser; -import cc.carm.lib.configuration.core.value.ValueManifest; -import cc.carm.lib.configuration.core.value.impl.CachedConfigValue; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class ConfiguredValue extends CachedConfigValue { - - public static ConfigValueBuilder builderOf(Class valueClass) { - return builder().asValue(valueClass); - } - - @SuppressWarnings("unchecked") - public static ConfiguredValue of(@NotNull V defaultValue) { - return of((Class) defaultValue.getClass(), defaultValue); - } - - public static ConfiguredValue of(Class valueClass) { - return of(valueClass, null); - } - - public static ConfiguredValue of(Class valueClass, @Nullable V defaultValue) { - return builderOf(valueClass).fromObject().defaults(defaultValue).build(); - } - - protected final @NotNull Class valueClass; - - protected final @NotNull ConfigValueParser parser; - protected final @NotNull ConfigDataFunction serializer; - - public ConfiguredValue(@NotNull ValueManifest manifest, @NotNull Class valueClass, - @NotNull ConfigValueParser parser, - @NotNull ConfigDataFunction serializer) { - super(manifest); - this.valueClass = valueClass; - this.parser = parser; - this.serializer = serializer; - } - - /** - * @return Value's type class - */ - public @NotNull Class getValueClass() { - return valueClass; - } - - /** - * @return Value's parser, cast value from base object. - */ - public @NotNull ConfigValueParser getParser() { - return parser; - } - - /** - * @return Value's serializer, serialize value to base object. - */ - public @NotNull ConfigDataFunction getSerializer() { - return serializer; - } - - @Override - public V get() { - if (!isExpired()) return getCachedOrDefault(); - // Data that is outdated and needs to be parsed again. - - Object value = getValue(); - if (value == null) return getDefaultValue(); // 获取的值不存在,直接使用默认值。 - try { - // If there are no errors, update the cache and return. - return updateCache(this.parser.parse(value, this.defaultValue)); - } catch (Exception e) { - // There was a parsing error, prompted and returned the default value. - e.printStackTrace(); - return getDefaultValue(); - } - - } - - /** - * Set the value of the configuration path. - * Will use {@link #getSerializer()} to serialize the value. - * - * @param value The value to be set - */ - @Override - public void set(V value) { - updateCache(value); - if (value == null) setValue(null); - else { - try { - setValue(serializer.parse(value)); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - -} - diff --git a/core/src/main/java/cc/carm/lib/configuration/core/function/ConfigDataFunction.java b/core/src/main/java/cc/carm/lib/configuration/function/DataFunction.java similarity index 75% rename from core/src/main/java/cc/carm/lib/configuration/core/function/ConfigDataFunction.java rename to core/src/main/java/cc/carm/lib/configuration/function/DataFunction.java index d40fda3..75a4708 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/function/ConfigDataFunction.java +++ b/core/src/main/java/cc/carm/lib/configuration/function/DataFunction.java @@ -1,4 +1,4 @@ -package cc.carm.lib.configuration.core.function; +package cc.carm.lib.configuration.function; import org.jetbrains.annotations.Contract; @@ -7,39 +7,39 @@ import org.jetbrains.annotations.NotNull; import java.util.Objects; @FunctionalInterface -public interface ConfigDataFunction { +public interface DataFunction { - @NotNull R parse(@NotNull T data) throws Exception; + @NotNull R handle(@NotNull T data) throws Exception; - default @NotNull ConfigDataFunction andThen(@NotNull ConfigDataFunction after) { + default @NotNull DataFunction andThen(@NotNull DataFunction after) { Objects.requireNonNull(after); - return data -> after.parse(parse(data)); + return data -> after.handle(handle(data)); } @Contract(pure = true) - static @NotNull ConfigDataFunction identity() { + static @NotNull DataFunction identity() { return input -> input; } @Contract(pure = true) - static @NotNull ConfigDataFunction identity(Class type) { + static @NotNull DataFunction identity(Class type) { return input -> input; } @Contract(pure = true) - static @NotNull ConfigDataFunction required() { + static @NotNull DataFunction required() { return input -> { throw new IllegalArgumentException("Please specify the value parser."); }; } @Contract(pure = true) - static @NotNull ConfigDataFunction toObject() { + static @NotNull DataFunction toObject() { return input -> input; } @Contract(pure = true) - static @NotNull ConfigDataFunction castObject(Class valueClass) { + static @NotNull DataFunction castObject(Class valueClass) { return input -> { if (valueClass.isInstance(input)) return valueClass.cast(input); else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName()); @@ -47,7 +47,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction castFromString(Class valueClass) { + static @NotNull DataFunction castFromString(Class valueClass) { return input -> { if (valueClass.isInstance(input)) return valueClass.cast(input); else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName()); @@ -55,7 +55,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction castToString() { + static @NotNull DataFunction castToString() { return input -> { if (input instanceof String) return (String) input; else if (input instanceof Enum) return ((Enum) input).name(); @@ -64,7 +64,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction parseString(Class valueClass) { + static @NotNull DataFunction parseString(Class valueClass) { return input -> { if (valueClass.isInstance(input)) return valueClass.cast(input); else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName()); @@ -72,7 +72,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction intValue() { + static @NotNull DataFunction intValue() { return input -> { if (input instanceof Integer) { return (Integer) input; @@ -83,7 +83,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction shortValue() { + static @NotNull DataFunction shortValue() { return input -> { if (input instanceof Short) { return (Short) input; @@ -94,7 +94,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction doubleValue() { + static @NotNull DataFunction doubleValue() { return input -> { if (input instanceof Double) { return (Double) input; @@ -105,7 +105,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction byteValue() { + static @NotNull DataFunction byteValue() { return input -> { if (input instanceof Byte) { return (Byte) input; @@ -116,7 +116,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction floatValue() { + static @NotNull DataFunction floatValue() { return input -> { if (input instanceof Float) { return (Float) input; @@ -127,7 +127,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction longValue() { + static @NotNull DataFunction longValue() { return input -> { if (input instanceof Long) { return (Long) input; @@ -138,7 +138,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction booleanValue() { + static @NotNull DataFunction booleanValue() { return input -> { if (input instanceof Boolean) { return (Boolean) input; diff --git a/core/src/main/java/cc/carm/lib/configuration/function/ValueHandler.java b/core/src/main/java/cc/carm/lib/configuration/function/ValueHandler.java new file mode 100644 index 0000000..6b67b61 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/function/ValueHandler.java @@ -0,0 +1,72 @@ +package cc.carm.lib.configuration.function; + + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; + +@FunctionalInterface +public interface ValueHandler { + + @Nullable R handle(@NotNull ConfigurationHolder holder, @NotNull T data) throws Exception; + + default ValueHandler andThen(@NotNull ValueHandler after) { + Objects.requireNonNull(after); + return ((provider, data) -> { + R result = handle(provider, data); + if (result == null) return null; + else return after.handle(provider, result); + }); + } + + default ValueHandler compose(@NotNull ValueHandler before) { + Objects.requireNonNull(before); + return ((provider, data) -> { + T result = before.handle(provider, data); + if (result == null) return null; + return handle(provider, result); + }); + } + + default ValueHandler compose(@NotNull DataFunction before) { + Objects.requireNonNull(before); + return ((provider, data) -> { + T result = before.handle(data); + return handle(provider, result); + }); + } + + @Contract(pure = true) + static @NotNull ValueHandler identity() { + return (provider, input) -> input; + } + + @Contract(pure = true) + static @NotNull ValueHandler toObject() { + return ConfigurationHolder::serialize; + } + + @Contract(pure = true) + static @NotNull ValueHandler stringValue() { + return (provider, input) -> String.valueOf(input); + } + + @Contract(pure = true) + static @NotNull ValueHandler fromObject(ValueType type) { + return (provider, input) -> provider.deserialize(type, input); + } + + + @Contract(pure = true) + static @NotNull ValueHandler required() { + return (provider, input) -> { + throw new IllegalArgumentException("Please specify the value parser."); + }; + } +} + + diff --git a/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationFactory.java b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationFactory.java new file mode 100644 index 0000000..8d93f88 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationFactory.java @@ -0,0 +1,118 @@ +package cc.carm.lib.configuration.source; + +import cc.carm.lib.configuration.adapter.*; +import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter; +import cc.carm.lib.configuration.adapter.strandard.StandardAdapters; +import cc.carm.lib.configuration.function.DataFunction; +import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; +import cc.carm.lib.configuration.source.loader.PathGenerator; +import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; +import cc.carm.lib.configuration.source.option.ConfigurationOption; +import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder; +import cc.carm.lib.configuration.source.section.ConfigureSource; +import org.jetbrains.annotations.NotNull; + +import java.lang.annotation.Annotation; +import java.util.function.Consumer; +import java.util.function.Function; + +public abstract class ConfigurationFactory< + SOURCE extends ConfigureSource, + HOLDER extends ConfigurationHolder, + SELF + > { + + protected ValueAdapterRegistry adapters = new ValueAdapterRegistry(); + protected ConfigurationOptionHolder options = new ConfigurationOptionHolder(); + protected ConfigurationInitializer initializer = new ConfigurationInitializer(); + + public ConfigurationFactory() { + this.adapters.register(PrimitiveAdapter.ADAPTERS); + this.adapters.register(StandardAdapters.SECTION_ADAPTER); + } + + public abstract SELF self(); + + + public SELF adapters(ValueAdapterRegistry adapters) { + this.adapters = adapters; + return self(); + } + + public SELF adapter(Consumer adapterRegistryConsumer) { + adapterRegistryConsumer.accept(adapters); + return self(); + } + + public SELF adapter(@NotNull ValueAdapter adapter) { + return adapter(a -> a.register(adapter)); + } + + public SELF adapter(@NotNull ValueType type, @NotNull ValueSerializer serializer) { + return adapter(a -> a.register(type, serializer)); + } + + public SELF adapter(@NotNull ValueType type, @NotNull ValueParser parser) { + return adapter(a -> a.register(type, parser)); + } + + public SELF adapter(@NotNull Class from, @NotNull Class to, + @NotNull DataFunction parser, + @NotNull DataFunction serializer) { + return adapter(a -> a.register(from, to, parser, serializer)); + } + + public SELF adapter(@NotNull ValueType from, @NotNull ValueType to, + @NotNull DataFunction parser, + @NotNull DataFunction serializer) { + return adapter(a -> a.register(from, to, parser, serializer)); + } + + public SELF adapter(@NotNull ValueType type, @NotNull ValueSerializer serializer, @NotNull ValueParser parser) { + return adapter(a -> a.register(type, serializer, parser)); + } + + public SELF adapter(@NotNull Class type, @NotNull ValueSerializer serializer, @NotNull ValueParser parser) { + return adapter(ValueType.of(type), serializer, parser); + } + + public SELF options(ConfigurationOptionHolder options) { + this.options = options; + return self(); + } + + public SELF option(Consumer optionsConsumer) { + optionsConsumer.accept(options); + return self(); + } + + public SELF option(ConfigurationOption option, O value) { + return option(o -> o.set(option, value)); + } + + + public SELF initializer(ConfigurationInitializer initializer) { + this.initializer = initializer; + return self(); + } + + public SELF initializer(Consumer initializerConsumer) { + initializerConsumer.accept(initializer); + return self(); + } + + public SELF pathGenerator(PathGenerator generator) { + return initializer(loader -> { + loader.pathGenerator(generator); + }); + } + + public SELF metaAnnotation(@NotNull Class annotation, + @NotNull ConfigurationMetadata metadata, + @NotNull Function extractor) { + return initializer(loader -> loader.registerAnnotation(annotation, metadata, extractor)); + } + + public abstract @NotNull HOLDER build(); + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationHolder.java b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationHolder.java new file mode 100644 index 0000000..94592ab --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationHolder.java @@ -0,0 +1,101 @@ +package cc.carm.lib.configuration.source; + +import cc.carm.lib.configuration.Configuration; +import cc.carm.lib.configuration.adapter.ValueAdapterRegistry; +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; +import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder; +import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder; +import cc.carm.lib.configuration.source.section.ConfigureSource; +import cc.carm.lib.configuration.value.ValueManifest; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public abstract class ConfigurationHolder> { + + protected final @NotNull ValueAdapterRegistry adapters; + protected final @NotNull ConfigurationOptionHolder options; + protected final @NotNull Map metadata; + + protected final @NotNull ConfigurationInitializer initializer; + + public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters, + @NotNull ConfigurationOptionHolder options, + @NotNull Map metadata, + @NotNull ConfigurationInitializer initializer) { + this.initializer = initializer; + this.adapters = adapters; + this.options = options; + this.metadata = metadata; + } + + public abstract @NotNull SOURCE config(); + + public void reload() throws Exception { + config().reload(); + } + + public void save() throws Exception { + config().save(); + } + + public ConfigurationOptionHolder options() { + return options; + } + + public @NotNull Map metadata() { + return this.metadata; + } + + public @NotNull ConfigurationMetaHolder metadata(@Nullable String path) { + return metadata().computeIfAbsent(path, k -> new ConfigurationMetaHolder()); + } + + + public ValueAdapterRegistry adapters() { + return this.adapters; + } + + public ConfigurationInitializer initializer() { + return initializer; + } + + @Contract("_,null -> null") + public T deserialize(@NotNull Class type, @Nullable Object source) throws Exception { + return adapters().deserialize(this, type, source); + } + + @Contract("_,null -> null") + public T deserialize(@NotNull ValueType type, @Nullable Object source) throws Exception { + return adapters().deserialize(this, type, source); + } + + @Contract("null -> null") + public Object serialize(@Nullable T value) throws Exception { + return adapters().serialize(this, value); + } + + public void initialize(Class configClass) { + try { + initializer.initialize(this, configClass); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void initialize(@NotNull Configuration config) { + try { + initializer.initialize(this, config); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void initialize(@NotNull ValueManifest value) { + value.holder(this); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigInitializeHandler.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigInitializeHandler.java new file mode 100644 index 0000000..1b1ba35 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigInitializeHandler.java @@ -0,0 +1,32 @@ +package cc.carm.lib.configuration.source.loader; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface ConfigInitializeHandler { + + static ConfigInitializeHandler start() { + return (provider, path, value) -> { + }; + } + + void whenInitialize(@NotNull ConfigurationHolder holder, @Nullable String path, @NotNull T value) throws Exception; + + default ConfigInitializeHandler andThen(ConfigInitializeHandler after) { + return (provider, path, value) -> { + whenInitialize(provider, path, value); + after.whenInitialize(provider, path, value); + }; + } + + default ConfigInitializeHandler compose(ConfigInitializeHandler before) { + return (provider, path, value) -> { + before.whenInitialize(provider, path, value); + whenInitialize(provider, path, value); + }; + } + + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationInitializer.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationInitializer.java new file mode 100644 index 0000000..a6f7f42 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationInitializer.java @@ -0,0 +1,183 @@ +package cc.carm.lib.configuration.source.loader; + +import cc.carm.lib.configuration.Configuration; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; +import cc.carm.lib.configuration.source.option.StandardOptions; +import cc.carm.lib.configuration.value.ConfigValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.function.Function; + +/** + * Configuration initializer, + * used to initialize {@link ConfigValue}s from {@link Configuration} classes. + */ +public class ConfigurationInitializer { + + protected @NotNull PathGenerator pathGenerator; + protected @NotNull ConfigInitializeHandler fieldInitializer; + protected @NotNull ConfigInitializeHandler> classInitializer; + + public ConfigurationInitializer() { + this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start()); + } + + public ConfigurationInitializer(@NotNull PathGenerator pathGenerator, + @NotNull ConfigInitializeHandler fieldInitializer, + @NotNull ConfigInitializeHandler> classInitializer) { + this.pathGenerator = pathGenerator; + this.fieldInitializer = fieldInitializer; + this.classInitializer = classInitializer; + } + + public void pathGenerator(@NotNull PathGenerator pathGenerator) { + this.pathGenerator = pathGenerator; + } + + public @NotNull PathGenerator pathGenerator() { + return pathGenerator; + } + + public ConfigInitializeHandler fieldInitializer() { + return fieldInitializer; + } + + public void fieldInitializer(@NotNull ConfigInitializeHandler fieldInitializer) { + this.fieldInitializer = fieldInitializer; + } + + public ConfigInitializeHandler> classInitializer() { + return classInitializer; + } + + public void classInitializer(@NotNull ConfigInitializeHandler> classInitializer) { + this.classInitializer = classInitializer; + } + + public void appendFieldInitializer(@NotNull ConfigInitializeHandler fieldInitializer) { + this.fieldInitializer = this.fieldInitializer.andThen(fieldInitializer); + } + + public void appendClassInitializer(@NotNull ConfigInitializeHandler> classInitializer) { + this.classInitializer = this.classInitializer.andThen(classInitializer); + } + + public void registerAnnotation(@NotNull Class annotation, + @NotNull ConfigurationMetadata metadata, + @NotNull Function extractor) { + appendFieldInitializer((holder, path, field) -> { + A data = field.getAnnotation(annotation); + if (data == null) return; + holder.metadata(path).setIfAbsent(metadata, extractor.apply(data)); + }); + appendClassInitializer((holder, path, clazz) -> { + A data = clazz.getAnnotation(annotation); + if (data == null) return; + holder.metadata(path).setIfAbsent(metadata, extractor.apply(data)); + }); + } + + + public @Nullable String getFieldPath(@NotNull ConfigurationHolder holder, @Nullable String parentPath, @NotNull Field field) { + return pathGenerator.getFieldPath(holder, parentPath, field); + } + + public @Nullable String getClassPath(@NotNull ConfigurationHolder holder, @Nullable String parentPath, + @NotNull Class clazz, @Nullable Field clazzField) { + return pathGenerator.getClassPath(holder, parentPath, clazz, clazzField); + } + + public void initialize(@NotNull ConfigurationHolder holder, + @NotNull Configuration config) throws Exception { + initializeInstance(holder, config, null, null); + if (holder.options().get(StandardOptions.SET_DEFAULTS)) holder.save(); + } + + public void initialize(@NotNull ConfigurationHolder holder, + @NotNull Class clazz) throws Exception { + initializeStaticClass(holder, clazz, null, null); + if (holder.options().get(StandardOptions.SET_DEFAULTS)) holder.save(); + } + + + // 针对实例类的初始化方法 + protected void initializeInstance(@NotNull ConfigurationHolder holder, @NotNull Configuration root, + @Nullable String parentPath, @Nullable Field configField) { + String path = getClassPath(holder, parentPath, root.getClass(), configField); + try { + this.classInitializer.whenInitialize(holder, path, root.getClass()); + } catch (Exception e) { + e.printStackTrace(); + } + Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(holder, root, field, path)); + } + + // 针对静态类的初始化方法 + @SuppressWarnings("unchecked") + protected void initializeStaticClass(@NotNull ConfigurationHolder holder, + @NotNull Class clazz, + @Nullable String parentPath, @Nullable Field configField) { + if (!Configuration.class.isAssignableFrom(clazz)) return; // Only Configuration class can be initialized. + + String path = getClassPath(holder, parentPath, clazz, configField); + + try { + this.classInitializer.whenInitialize(holder, path, (Class) clazz); + } catch (Exception e) { + e.printStackTrace(); + } + + for (Field field : clazz.getDeclaredFields()) { + initializeField(holder, clazz, field, path); + } + + if (holder.options().get(StandardOptions.LOAD_SUB_CLASSES)) { + Class[] classes = clazz.getDeclaredClasses(); + for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。 + initializeStaticClass(holder, classes[i], path, null); + } + } + } + + protected void initializeField(@NotNull ConfigurationHolder holder, + @NotNull Object source, @NotNull Field field, @Nullable String parent) { + try { + field.setAccessible(true); + Object object = field.get(source); +// + if (object instanceof ConfigValue) { + // 目标是 ConfigValue 实例,进行具体的初始化注入 + ConfigValue value = (ConfigValue) object; + String path = getFieldPath(holder, parent, field); + if (path == null) return; + value.initialize(holder, path); + try { + this.fieldInitializer.whenInitialize(holder, path, field); + } catch (Exception e) { + e.printStackTrace(); + } + if (holder.options().get(StandardOptions.SET_DEFAULTS)) { + value.setDefault(); + } + } else if (source instanceof Configuration && object instanceof Configuration) { + // 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。 + initializeInstance(holder, (Configuration) object, parent, field); + } else if (source instanceof Class && object instanceof Class) { + // 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。 + initializeStaticClass(holder, (Class) object, parent, field); + } + + // 以上判断实现以下规范: + // - 实例类中仅加载 ConfigValue实例 与 Configuration实例 + // - 静态类中仅加载 静态ConfigValue实例 与 静态Configuration类 + + } catch (IllegalAccessException ignored) { + } + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java new file mode 100644 index 0000000..fc2e260 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java @@ -0,0 +1,114 @@ +package cc.carm.lib.configuration.source.loader; + +import cc.carm.lib.configuration.annotation.ConfigPath; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.option.StandardOptions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.lang.reflect.Field; +import java.util.function.UnaryOperator; + +public class PathGenerator { + + public static PathGenerator of() { + return of(PathGenerator::covertPathName); + } + + public static PathGenerator of(UnaryOperator pathConverter) { + return new PathGenerator(pathConverter); + } + + protected UnaryOperator pathConverter; + + public PathGenerator(UnaryOperator pathConverter) { + this.pathConverter = pathConverter; + } + + public @NotNull UnaryOperator getPathConverter() { + return pathConverter; + } + + public void setPathConverter(UnaryOperator pathConverter) { + this.pathConverter = pathConverter; + } + + public String covertPath(String name) { + return pathConverter.apply(name); + } + + public @Nullable String getFieldPath(@NotNull ConfigurationHolder holder, + @Nullable String parentPath, @NotNull Field field) { + ConfigPath path = field.getAnnotation(ConfigPath.class); + if (path == null) + return link(holder, parentPath, false, covertPath(field.getName())); // No annotation, use field name. + else return link(holder, parentPath, path.root(), select(path.value(), covertPath(field.getName()))); + } + + public @Nullable String getClassPath(@NotNull ConfigurationHolder holder, + @Nullable String parentPath, @NotNull Class clazz, @Nullable Field clazzField) { + // For standard path generator, we generate path following by: + // 1. Check if the class has a ConfigPath annotation, if so, use the root and value as the path. + // 2. If the class defined as a field, check if the field has a ConfigPath annotation, + // and use filed information. + ConfigPath clazzPath = clazz.getAnnotation(ConfigPath.class); + if (clazzPath != null) return link(holder, parentPath, clazzPath.root(), clazzPath.value()); + + if (clazzField == null) { + return link(holder, parentPath, false, parentPath == null ? null : covertPath(clazz.getSimpleName())); // No field, use class name. + } + + ConfigPath fieldPath = clazzField.getAnnotation(ConfigPath.class); + if (fieldPath == null) return link(holder, parentPath, false, covertPath(clazzField.getName())); + else return getFieldPath(holder, parentPath, clazzField); + } + + protected String select(String path, String defaultValue) { + if (path == null || path.isEmpty()) return defaultValue; + else return isBlank(path) ? null : path; + } + + protected @Nullable String link(@NotNull ConfigurationHolder holder, + @Nullable String parent, boolean root, @Nullable String path) { + if (path == null || path.isEmpty()) return root ? null : parent; + return root || parent == null ? path : parent + pathSeparator(holder) + path; + } + + public static boolean isBlank(String path) { + return path == null || path.replace(" ", "").isEmpty(); + } + + public static char pathSeparator(ConfigurationHolder holder) { + return holder.options().get(StandardOptions.PATH_SEPARATOR); + } + + /** + * Get the configuration name of the specified element. + * Use the naming convention of all lowercase and "-" links. + *

+ * e.g. "SOME_NAME" -> "some-name" + * + * @param name source name + * @return the final path + */ + public static String covertPathName(String name) { + return name + // Replace all uppercase letters with dashes + .replaceAll("[A-Z]", "=$0") + // If the first letter is also capitalized, + // it will also be converted and the first dash will need to be removed + .replaceAll("^=(.*)$", "$1") + // Because the name may contain _, it needs to be treated a little differently + .replaceAll("_=([A-Z])", "_$1") + // The content that is not named in all caps is then converted + .replaceAll("([a-z])=([A-Z])", "$1_$2") + // Remove any extra horizontal lines + .replaceAll("=", "") + // Replace the underscore with a dash + .replace("_", "-") + // Finally, convert it to all lowercase + .toLowerCase(); + } + + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetaHolder.java b/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetaHolder.java new file mode 100644 index 0000000..fce23be --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetaHolder.java @@ -0,0 +1,124 @@ +package cc.carm.lib.configuration.source.meta; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class ConfigurationMetaHolder { + + protected final @NotNull Map, Object> values; + + public ConfigurationMetaHolder() { + this(new ConcurrentHashMap<>()); + } + + public ConfigurationMetaHolder(@NotNull Map, Object> values) { + this.values = values; + } + + public @NotNull Map, Object> values() { + return values; + } + + /** + * Get the value of option. + * + * @param type {@link ConfigurationMetadata} + * @param defaultValue Default value if the value of option is not set. + * @param Value type + * @return Value of option + */ + @SuppressWarnings("unchecked") + @Contract("_,!null -> !null") + public @Nullable V get(@NotNull ConfigurationMetadata type, @Nullable V defaultValue) { + return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue)); + } + + /** + * Get the value of option. + * + * @param type {@link ConfigurationMetadata} + * @param defaultValue Default value if the value of option is not set. + * @param Value type + * @return Value of option + */ + @SuppressWarnings("unchecked") + @Contract("_,!null -> !null") + public @Nullable V get(@NotNull ConfigurationMetadata type, Supplier<@Nullable V> defaultValue) { + return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue)); + } + + /** + * Get the value of option. + * + * @param type {@link ConfigurationMetadata} + * @param Value type + * @return Value of option + */ + public @Nullable V get(@NotNull ConfigurationMetadata type) { + return get(type, (V) null); + } + + public boolean contains(@NotNull ConfigurationMetadata type) { + return values().containsKey(type) || type.hasDefaults(); + } + + /** + * Set the value of meta, if the value is null, the meta will be removed. + *
Will only be changed in current holder. + * + * @param type {@link ConfigurationMetadata} + * @param value Value of meta + * @param Value type + * @return Previous value of meta + */ + @SuppressWarnings("unchecked") + public @Nullable V set(@NotNull ConfigurationMetadata type, @Nullable V value) { + if (value == null || type.isDefault(value)) { + return (V) values().remove(type); + } else { + return (V) values().put(type, value); + } + } + + /** + * Set the value of meta, if the value is null, the meta will not be changed. + *
Will only be changed in current holder. + * + * @param type {@link ConfigurationMetadata} + * @param value Value of meta + * @param Value type + */ + public void setIfAbsent(@NotNull ConfigurationMetadata type, @Nullable V value) { + if (value == null || type.isDefault(value)) { + values().remove(type); + } else { + values().putIfAbsent(type, value); + } + } + + /** + * Set the value of meta, if the value is null, the meta will not be changed. + *
Will only be changed in current holder. + * + * @param type {@link ConfigurationMetadata} + * @param value Value of meta + * @param Value type + */ + @SuppressWarnings("unchecked") + public @Nullable V setIfPresent(@NotNull ConfigurationMetadata type, @Nullable V value) { + Object exists = values().get(type); + if (exists == null) return null; + + if (value == null || type.isDefault(value)) { + return (V) values().remove(type); + } else { + return (V) values().put(type, value); + } + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetadata.java b/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetadata.java new file mode 100644 index 0000000..ff28804 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetadata.java @@ -0,0 +1,57 @@ +package cc.carm.lib.configuration.source.meta; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +public class ConfigurationMetadata { + + public static ConfigurationMetadata of() { + return of(() -> null); + } + + public static ConfigurationMetadata of(T defaults) { + return of(() -> defaults); + } + + public static ConfigurationMetadata of(@NotNull Supplier<@Nullable T> defaults) { + return new ConfigurationMetadata<>(defaults); + } + + protected Supplier<@Nullable T> defaultsSupplier; + + public ConfigurationMetadata(@NotNull Supplier<@Nullable T> defaults) { + this.defaultsSupplier = defaults; + } + + public boolean isDefault(@NotNull T value) { + return value.equals(defaults()); + } + + public boolean hasDefaults() { + return defaults() != null; + } + + public T defaultOrSupply(@Nullable T suppliedValue) { + return defaultOrSupply(() -> suppliedValue); + } + + public T defaultOrSupply(Supplier<@Nullable T> suppliedValue) { + T defaults = defaults(); + return defaults == null ? suppliedValue.get() : defaults; + } + + public @Nullable T defaults() { + return defaultsSupplier.get(); + } + + public void setDefaults(Supplier defaultFunction) { + this.defaultsSupplier = defaultFunction; + } + + public void setDefaults(T value) { + setDefaults(() -> value); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOption.java b/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOption.java new file mode 100644 index 0000000..fdc6ed4 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOption.java @@ -0,0 +1,46 @@ +package cc.carm.lib.configuration.source.option; + +import org.jetbrains.annotations.NotNull; + +public class ConfigurationOption { + + @SuppressWarnings("unchecked") + public static ConfigurationOption of(@NotNull T defaultValue) { + return of((Class) defaultValue.getClass(), defaultValue); + } + + public static ConfigurationOption of(@NotNull Class valueClazz, @NotNull T defaultValue) { + return new ConfigurationOption<>(valueClazz, defaultValue); + } + + private final @NotNull Class valueClazz; + private @NotNull V defaultValue; + + public ConfigurationOption(@NotNull Class valueClazz, @NotNull V defaultValue) { + this.valueClazz = valueClazz; + this.defaultValue = defaultValue; + } + + @NotNull + public Class valueClass() { + return this.valueClazz; + } + + public @NotNull V defaults() { + return defaultValue; + } + + /** + * Set the default value of option. + * + * @param defaultValue Default value + */ + public void defaults(@NotNull V defaultValue) { + this.defaultValue = defaultValue; + } + + public boolean isDefault(@NotNull V value) { + return value.equals(defaultValue); + } + +} \ No newline at end of file diff --git a/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptionHolder.java b/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptionHolder.java new file mode 100644 index 0000000..00f5c5b --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptionHolder.java @@ -0,0 +1,70 @@ +package cc.carm.lib.configuration.source.option; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +public class ConfigurationOptionHolder { + + public static @NotNull ConfigurationOptionHolder of(@NotNull Map, Object> options) { + return new ConfigurationOptionHolder(new ConcurrentHashMap<>(options)); + } + + protected final Map, Object> options; + + public ConfigurationOptionHolder() { + this(new ConcurrentHashMap<>()); + } + + public ConfigurationOptionHolder(Map, Object> options) { + this.options = options; + } + + public @NotNull Map, Object> values() { + return options; + } + + /** + * Get the value of option. + * + * @param type {@link ConfigurationOption} + * @param Value type + * @return Value of option + */ + @SuppressWarnings("unchecked") + public @NotNull V get(@NotNull ConfigurationOption type) { + return Optional.ofNullable(values().get(type)).map(v -> (V) v).orElseGet(type::defaults); + } + + /** + * Set the value of option. + * + * @param type {@link ConfigurationOption} + * @param value Value of option + * @param Value type + * @return Previous value of option + */ + @SuppressWarnings("unchecked") + public @Nullable V set(@NotNull ConfigurationOption type, @Nullable V value) { + if (value == null) { + return (V) values().remove(type); + } else { + return (V) values().put(type, value); + } + } + + /** + * Set the value of option to option's {@link ConfigurationOption#defaults()}. + * + * @param type {@link ConfigurationOption} + * @param Value type + * @return Previous value of option + */ + public @Nullable V clear(@NotNull ConfigurationOption type) { + return set(type, null); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/option/StandardOptions.java b/core/src/main/java/cc/carm/lib/configuration/source/option/StandardOptions.java new file mode 100644 index 0000000..1da01ad --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/option/StandardOptions.java @@ -0,0 +1,34 @@ +package cc.carm.lib.configuration.source.option; + +import cc.carm.lib.configuration.Configuration; +import cc.carm.lib.configuration.source.ConfigurationHolder; + +import static cc.carm.lib.configuration.source.option.ConfigurationOption.of; + +public interface StandardOptions { + + /** + * The configuration path separator. + */ + ConfigurationOption PATH_SEPARATOR = of('.'); + + /** + * Whether to set & save default values if offered and not exists in configuration. + */ + ConfigurationOption SET_DEFAULTS = of(true); + + /** + * Whether to load subclasses of configuration class. + */ + ConfigurationOption LOAD_SUB_CLASSES = of(true); + + /** + * Whether to pre parse the config values. + *
if false, the values will be parsed when calling + * {@link cc.carm.lib.configuration.value.ConfigValue#get()} + *
if true, the values will be parsed when + * {@link ConfigurationHolder#initialize(Configuration)}. + */ + ConfigurationOption PRELOAD = of(false); + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSection.java b/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSection.java new file mode 100644 index 0000000..8da24a1 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSection.java @@ -0,0 +1,323 @@ +package cc.carm.lib.configuration.source.section; + +import cc.carm.lib.configuration.function.DataFunction; +import cc.carm.lib.configuration.source.option.StandardOptions; +import org.jetbrains.annotations.*; + +import java.util.*; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +public interface ConfigureSection { + + @NotNull ConfigureSource source(); + + @Nullable ConfigureSection parent(); + + default char separator() { + return source().holder().options().get(StandardOptions.PATH_SEPARATOR); + } + + @NotNull + @UnmodifiableView + default Set getKeys(boolean deep) { + return getValues(deep).keySet(); + } + + @NotNull + @UnmodifiableView + Map getValues(boolean deep); + + void set(@NotNull String path, @Nullable Object value); + + boolean contains(@NotNull String path); + + default boolean isType(@NotNull String path, @NotNull Class typeClass) { + return typeClass.isInstance(get(path)); + } + + boolean isList(@NotNull String path); + + @Nullable List getList(@NotNull String path); + + boolean isSection(@NotNull String path); + + @Nullable + ConfigureSection getSection(@NotNull String path); + + @Nullable Object get(@NotNull String path); + + default @Nullable T get(@NotNull String path, @NotNull Class clazz) { + return get(path, null, clazz); + } + + default @Nullable T get(@NotNull String path, @NotNull DataFunction parser) { + return get(path, null, parser); + } + + @Contract("_,!null,_->!null") + default @Nullable T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class clazz) { + return get(path, defaultValue, DataFunction.castObject(clazz)); + } + + @Contract("_,!null,_->!null") + default @Nullable T get(@NotNull String path, @Nullable T defaultValue, + @NotNull DataFunction parser) { + Object value = get(path); + if (value != null) { + try { + return parser.handle(value); + } catch (Exception e) { + e.printStackTrace(); + } + } + return defaultValue; + } + + + default boolean isBoolean(@NotNull String path) { + return isType(path, Boolean.class); + } + + default boolean getBoolean(@NotNull String path) { + return getBoolean(path, false); + } + + @Contract("_, !null -> !null") + default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) { + return get(path, def, DataFunction.booleanValue()); + } + + default @Nullable Boolean isByte(@NotNull String path) { + return isType(path, Byte.class); + } + + default @Nullable Byte getByte(@NotNull String path) { + return getByte(path, (byte) 0); + } + + @Contract("_, !null -> !null") + default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) { + return get(path, def, DataFunction.byteValue()); + } + + default boolean isShort(@NotNull String path) { + return isType(path, Short.class); + } + + default @Nullable Short getShort(@NotNull String path) { + return getShort(path, (short) 0); + } + + @Contract("_, !null -> !null") + default @Nullable Short getShort(@NotNull String path, @Nullable Short def) { + return get(path, def, DataFunction.shortValue()); + } + + + default boolean isInt(@NotNull String path) { + return isType(path, Integer.class); + } + + default @Nullable Integer getInt(@NotNull String path) { + return getInt(path, 0); + } + + @Contract("_, !null -> !null") + default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) { + return get(path, def, DataFunction.intValue()); + } + + + default boolean isLong(@NotNull String path) { + return isType(path, Long.class); + } + + default @Nullable Long getLong(@NotNull String path) { + return getLong(path, 0L); + } + + @Contract("_, !null -> !null") + default @Nullable Long getLong(@NotNull String path, @Nullable Long def) { + return get(path, def, DataFunction.longValue()); + } + + + default boolean isFloat(@NotNull String path) { + return isType(path, Float.class); + } + + default @Nullable Float getFloat(@NotNull String path) { + return getFloat(path, 0.0F); + } + + @Contract("_, !null -> !null") + default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) { + return get(path, def, DataFunction.floatValue()); + } + + + default boolean isDouble(@NotNull String path) { + return isType(path, Double.class); + } + + default @Nullable Double getDouble(@NotNull String path) { + return getDouble(path, 0.0D); + } + + @Contract("_, !null -> !null") + default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) { + return get(path, def, DataFunction.doubleValue()); + } + + + default boolean isChar(@NotNull String path) { + return isType(path, Boolean.class); + } + + default @Nullable Character getChar(@NotNull String path) { + return getChar(path, null); + } + + @Contract("_, !null -> !null") + default @Nullable Character getChar(@NotNull String path, @Nullable Character def) { + return get(path, def, Character.class); + } + + + default boolean isString(@NotNull String path) { + return isType(path, String.class); + } + + default @Nullable String getString(@NotNull String path) { + return getString(path, null); + } + + @Contract("_, !null -> !null") + default @Nullable String getString(@NotNull String path, @Nullable String def) { + return get(path, def, String.class); + } + + /** + * Get a list of values from the section + *

+ * If the path does not exist, an empty list will be returned + *
Any changes please use {@link #set(String, Object)} after changes + * + * @param path The path to get the list from + * @param parser The function to parse the values + * @param The type of the values + * @return The list of values + */ + default @NotNull List getList(@NotNull String path, @NotNull DataFunction parser) { + return getCollection(path, ArrayList::new, parser); + } + + /** + * Get a list of strings from the section + *

Limitations see {@link #getList(String, DataFunction)} + * + * @param path The path to get the list from + * @return The list of strings + */ + default @NotNull List getStringList(@NotNull String path) { + return getList(path, DataFunction.castToString()); + } + + /** + * Get a list of integer from the section + *

Limitations see {@link #getList(String, DataFunction)} + * + * @param path The path to get the list from + * @return The list of int values + */ + default @NotNull List getIntegerList(@NotNull String path) { + return getList(path, DataFunction.intValue()); + } + + /** + * Get a list of long from the section + *

Limitations see {@link #getList(String, DataFunction)} + * + * @param path The path to get the list from + * @return The list of long values + */ + default @NotNull List getLongList(@NotNull String path) { + return getList(path, DataFunction.longValue()); + } + + /** + * Get a list of double from the section + *

Limitations see {@link #getList(String, DataFunction)} + * + * @param path The path to get the list from + * @return The list of doubles + */ + default @NotNull List getDoubleList(@NotNull String path) { + return getList(path, DataFunction.doubleValue()); + } + + /** + * Get a list of floats from the section + *

Limitations see {@link #getList(String, DataFunction)} + * + * @param path The path to get the list from + * @return The list of floats + */ + default @NotNull List getFloatList(@NotNull String path) { + return getList(path, DataFunction.floatValue()); + } + + /** + * Get a list of bytes from the section + *

Limitations see {@link #getList(String, DataFunction)} + * + * @param path The path to get the list from + * @return The list of bytes + */ + default @NotNull List getByteList(@NotNull String path) { + return getList(path, DataFunction.byteValue()); + } + + /** + * Get a list of char from the section + *

Limitations see {@link #getList(String, DataFunction)} + * + * @param path The path to get the list from + * @return The list of char + */ + default @NotNull List getCharList(@NotNull String path) { + return getList(path, DataFunction.castObject(Character.class)); + } + + default > @NotNull C getCollection(@NotNull String path, + @NotNull Supplier constructor, + @NotNull DataFunction parser) { + return parseCollection(getList(path), constructor, parser); + } + + default @NotNull Stream stream(@NotNull String path) { + List values = getList(path); + return values == null ? Stream.empty() : values.stream(); + } + + default @NotNull Stream stream(@NotNull String path, @NotNull Function parser) { + return stream(path).map(parser); + } + + static > @NotNull C parseCollection( + @Nullable List data, @NotNull Supplier constructor, + @NotNull DataFunction parser + ) { + C values = constructor.get(); + if (data == null) return values; + for (Object obj : data) { + try { + values.add(parser.handle(obj)); + } catch (Exception ignored) { + } + } + return values; + } +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSource.java b/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSource.java new file mode 100644 index 0000000..f0c83c7 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSource.java @@ -0,0 +1,102 @@ +package cc.carm.lib.configuration.source.section; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +public abstract class ConfigureSource< + SECTION extends ConfigureSection, ORIGINAL, + SELF extends ConfigureSource> + implements ConfigureSection { + + protected final @NotNull ConfigurationHolder holder; + protected long lastUpdateMillis; + + protected ConfigureSource(@NotNull ConfigurationHolder holder, long lastUpdateMillis) { + this.holder = holder; + this.lastUpdateMillis = lastUpdateMillis; + } + + public @NotNull ConfigurationHolder holder() { + return holder; + } + + + public void reload() throws Exception { + onReload(); // 调用重写的Reload方法 + this.lastUpdateMillis = System.currentTimeMillis(); + } + + protected abstract SELF self(); + + /** + * @return Original configuration object + */ + public abstract @NotNull ORIGINAL original(); + + /** + * @return The root {@link ConfigureSection} + */ + public abstract @NotNull SECTION section(); + + public abstract void save() throws Exception; + + protected abstract void onReload() throws Exception; + + public long getLastUpdateMillis() { + return this.lastUpdateMillis; + } + + public boolean isExpired(long parsedTime) { + return getLastUpdateMillis() > parsedTime; + } + + @Override + public @Nullable ConfigureSection parent() { + return null; // Source also represents the root section, so it has no parent + } + + @Override + public @NotNull Map getValues(boolean deep) { + return section().getValues(deep); + } + + @Override + public void set(@NotNull String path, @Nullable Object value) { + section().set(path, value); + } + + @Override + public boolean contains(@NotNull String path) { + return section().contains(path); + } + + @Override + public boolean isList(@NotNull String path) { + return section().isList(path); + } + + @Override + public @Nullable List getList(@NotNull String path) { + return section().getList(path); + } + + @Override + public boolean isSection(@NotNull String path) { + return section().isSection(path); + } + + @Override + public @Nullable ConfigureSection getSection(@NotNull String path) { + return section().getSection(path); + } + + @Override + public @Nullable Object get(@NotNull String path) { + return section().get(path); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/value/ConfigValue.java b/core/src/main/java/cc/carm/lib/configuration/value/ConfigValue.java new file mode 100644 index 0000000..70557d6 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/value/ConfigValue.java @@ -0,0 +1,95 @@ +package cc.carm.lib.configuration.value; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.Optional; + +public abstract class ConfigValue extends ValueManifest { + + protected ConfigValue(@NotNull ValueManifest manifest) { + super(manifest); + } + + /** + * 得到该配置的设定值(即读取到的值)。 + *
若初始化时未写入默认值,则可以通过 {@link #getOrDefault()} 方法在该设定值为空时获取默认值。 + * + * @return 设定值 + */ + public abstract @Nullable T get(); + + /** + * 得到该配置的设定值,若不存在,则返回默认值。 + * + * @return 设定值或默认值 + */ + public T getOrDefault() { + return optional().orElse(defaults()); + } + + /** + * 得到该配置的非空值。 + * + * @return 非空值 + * @throws NullPointerException 对应数据为空时抛出 + */ + public @NotNull T getNotNull() { + return Objects.requireNonNull(getOrDefault(), "Value(" + type() + ") @[" + path() + "] is null."); + } + + public @NotNull Optional<@Nullable T> optional() { + return Optional.ofNullable(get()); + } + + /** + * 设定该配置的值。 + *
设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationHolder#save()} 方法。 + * + * @param value 配置的值 + */ + public abstract void set(@Nullable T value); + + /** + * 初始化该配置的默认值。 + *
设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationHolder#save()} 方法。 + */ + public void setDefault() { + setDefault(false); + } + + /** + * 将该配置的值设置为默认值。 + *
设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationHolder#save()} 方法。 + * + * @param override 是否覆盖已设定的值 + */ + public void setDefault(boolean override) { + if (!override && config().contains(path())) return; + Optional.ofNullable(defaults()).ifPresent(this::set); + } + + /** + * 判断加载的配置是否与默认值相同。 + * + * @return 获取当前值是否为默认值。 + */ + public boolean isDefault() { + return Objects.equals(defaults(), get()); + } + + /** + * Try to save the configuration. + *
To save multiple modifications, + * it is recommended to call {@link ConfigurationHolder#save()} + * after all modifications are completed instead of this. + * + * @throws Exception + */ + public void save() throws Exception { + holder().save(); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/value/ValueManifest.java b/core/src/main/java/cc/carm/lib/configuration/value/ValueManifest.java new file mode 100644 index 0000000..ca48ab6 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/value/ValueManifest.java @@ -0,0 +1,124 @@ +package cc.carm.lib.configuration.value; + +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder; +import cc.carm.lib.configuration.source.section.ConfigureSource; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +public class ValueManifest { + + protected final @NotNull ValueType type; + protected final @NotNull BiConsumer<@NotNull ConfigurationHolder, @NotNull String> initializer; + + protected @Nullable ConfigurationHolder holder; + protected @Nullable String path; // Section path + + protected @NotNull Supplier<@Nullable T> defaultSupplier; + + + public ValueManifest(@NotNull ValueType type) { + this(type, () -> null, EMPTY_INITIALIZER, null, null); + } + + public ValueManifest(@NotNull T defaultValue) { + this(ValueType.of(defaultValue), () -> defaultValue); + } + + public ValueManifest(@NotNull ValueType type, @NotNull Supplier<@Nullable T> defaultSupplier) { + this(type, defaultSupplier, EMPTY_INITIALIZER, null, null); + } + + public ValueManifest(@NotNull ValueType type, @NotNull Supplier<@Nullable T> defaultSupplier, + @NotNull BiConsumer<@NotNull ConfigurationHolder, @NotNull String> initializer) { + this(type, defaultSupplier, initializer, null, null); + } + + public ValueManifest(@NotNull ValueType type, @NotNull Supplier<@Nullable T> defaultSupplier, + @NotNull BiConsumer<@NotNull ConfigurationHolder, @NotNull String> initializer, + @Nullable ConfigurationHolder holder, @Nullable String path) { + this.type = type; + this.initializer = initializer; + this.defaultSupplier = defaultSupplier; + this.holder = holder; + this.path = path; + initialize(); + } + + protected ValueManifest(@NotNull ValueManifest manifest) { + this(manifest.type, manifest.defaultSupplier, manifest.initializer, manifest.holder, manifest.path); + } + + public void initialize(@NotNull ConfigurationHolder holder, @NotNull String path) { + this.holder = holder; + this.path = path; + initialize(); + } + + protected void initialize() { + if (holder != null && path != null) this.initializer.accept(holder, path); + } + + public @NotNull ValueType type() { + return this.type; + } + + public void holder(@NotNull ConfigurationHolder holder) { + this.holder = holder; + } + + public void path(@NotNull String path) { + this.path = path; + } + + public @Nullable T defaults() { + return this.defaultSupplier.get(); + } + + public void defaults(@Nullable T defaultValue) { + defaults(() -> defaultValue); + } + + public void defaults(@NotNull Supplier<@Nullable T> defaultValue) { + this.defaultSupplier = defaultValue; + } + + public boolean hasDefaults() { + return this.defaultSupplier.get() != null; + } + + public @NotNull String path() { + if (path != null) return path; + else throw new IllegalStateException("No section path provided."); + } + + public @NotNull ConfigurationHolder holder() { + if (this.holder != null) return this.holder; + throw new IllegalStateException("Value does not have a provider."); + } + + public @NotNull ConfigureSource config() { + return holder().config(); + } + + public ConfigurationMetaHolder metadata() { + return holder().metadata(path()); + } + + protected Object getData() { + return config().get(path()); + } + + protected void setData(@Nullable Object value) { + config().set(path(), value); + } + + + private static final @NotNull BiConsumer<@NotNull ConfigurationHolder, @NotNull String> EMPTY_INITIALIZER = (provider, path) -> { + }; + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/value/impl/CachedConfigValue.java b/core/src/main/java/cc/carm/lib/configuration/value/impl/CachedConfigValue.java new file mode 100644 index 0000000..802a00a --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/value/impl/CachedConfigValue.java @@ -0,0 +1,81 @@ +package cc.carm.lib.configuration.value.impl; + +import cc.carm.lib.configuration.adapter.ValueAdapter; +import cc.carm.lib.configuration.adapter.ValueParser; +import cc.carm.lib.configuration.adapter.ValueSerializer; +import cc.carm.lib.configuration.value.ConfigValue; +import cc.carm.lib.configuration.value.ValueManifest; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public abstract class CachedConfigValue extends ConfigValue { + + protected @Nullable T cachedValue; + protected long parsedTime = -1; + + protected CachedConfigValue(@NotNull ValueManifest manifest) { + super(manifest); + } + + protected T updateCache(T value) { + this.parsedTime = System.currentTimeMillis(); + this.cachedValue = value; + return getCachedValue(); + } + + public @Nullable T getCachedValue() { + return cachedValue; + } + + public boolean cacheExpired() { + return this.parsedTime <= 0 || config().isExpired(this.parsedTime); + } + + protected final T getDefaultFirst(@Nullable T value) { + return updateCache(this.defaults() == null ? value : this.defaults()); + } + + /** + * Get the cached value or the default value if the cached value is null + * + * @return the cached value or the default value + */ + protected @Nullable T getCachedOrDefault() { + return getCachedOrDefault(null); + } + + /** + * Get the cached value or the default value if the cached value is null + * + * @param emptyValue the value to return if the cached value and the default value are null + * @return the cached value or the default value + */ + @Contract("!null->!null") + protected T getCachedOrDefault(@Nullable T emptyValue) { + if (getCachedValue() != null) return getCachedValue(); + else if (defaults() != null) return defaults(); + else return emptyValue; + } + + /** + * @return Value's parser, parse base object to value. + */ + protected @Nullable ValueParser parserFor(@NotNull ValueAdapter adapter) { + if (adapter.parser() != null) return adapter.parser(); + ValueAdapter registered = holder().adapters().adapterOf(adapter.type()); + if (registered == null) return null; + return registered.parser(); + } + + /** + * @return Value's serializer, parse value to base object. + */ + protected @Nullable ValueSerializer serializerFor(@NotNull ValueAdapter adapter) { + if (adapter.serializer() != null) return adapter.serializer(); + ValueAdapter registered = holder().adapters().adapterOf(adapter.type()); + if (registered == null) return null; + return registered.serializer(); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredList.java b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredList.java new file mode 100644 index 0000000..5cebea2 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredList.java @@ -0,0 +1,252 @@ +package cc.carm.lib.configuration.value.standard; + +import cc.carm.lib.configuration.adapter.ValueAdapter; +import cc.carm.lib.configuration.adapter.ValueParser; +import cc.carm.lib.configuration.adapter.ValueSerializer; +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.builder.list.ConfigListBuilder; +import cc.carm.lib.configuration.value.ValueManifest; +import cc.carm.lib.configuration.value.impl.CachedConfigValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ConfiguredList extends CachedConfigValue> implements List { + + public static @NotNull ConfigListBuilder builderOf(@NotNull Class type) { + return builderOf(ValueType.of(type)); + } + + public static @NotNull ConfigListBuilder builderOf(@NotNull ValueType type) { + return new ConfigListBuilder<>(type); + } + + protected final @NotNull Supplier> constructor; + protected final @NotNull ValueAdapter paramAdapter; + + public ConfiguredList(@NotNull ValueManifest> manifest, + @NotNull Supplier> constructor, + @NotNull ValueAdapter paramAdapter) { + super(manifest); + this.constructor = constructor; + this.paramAdapter = paramAdapter; + } + + /** + * @return Adapter of this value. + */ + public @NotNull ValueAdapter adapter() { + return this.paramAdapter; + } + + public @NotNull ValueType paramType() { + return adapter().type(); + } + + /** + * @return Value's parser, parse base object to value. + */ + public @Nullable ValueParser parser() { + return parserFor(adapter()); + } + + /** + * @return Value's serializer, parse value to base object. + */ + public @Nullable ValueSerializer serializer() { + return serializerFor(adapter()); + } + + private @NotNull List createList() { + return constructor.get(); + } + + @Override + public @NotNull List get() { + if (!cacheExpired()) return getCachedOrDefault(createList()); + // Data that is outdated and needs to be parsed again. + List list = createList(); + List data = config().contains(path()) ? config().getList(path()) : null; + if (data == null) return getDefaultFirst(list); + + ValueParser parser = parser(); + if (parser == null) return getDefaultFirst(list); + + for (Object dataVal : data) { + if (dataVal == null) continue; + try { + list.add(parser.parse(holder(), paramType(), dataVal)); + } catch (Exception e) { + e.printStackTrace(); + } + } + return updateCache(list); + } + + @Override + public void set(@Nullable List value) { + updateCache(value); + if (value == null) { + setData(null); + return; + } + + ValueSerializer serializer = serializer(); + if (serializer == null) return; + List data = new ArrayList<>(); + for (V val : value) { + if (val == null) continue; + try { + data.add(serializer.serialize(holder(), paramType(), val)); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + setData(data); + } + + @Override + public V get(int index) { + return getNotNull().get(index); + } + + public @NotNull List copy() { + return new ArrayList<>(getNotNull()); + } + + public @NotNull T handle(Function, T> function) { + List list = getNotNull(); + T result = function.apply(list); + set(list); + return result; + } + + public @NotNull ConfiguredList modify(Consumer> consumer) { + List list = getNotNull(); + consumer.accept(list); + set(list); + return this; + } + + @Override + public V set(int index, V element) { + return handle(list -> list.set(index, element)); + } + + @Override + public int size() { + return getNotNull().size(); + } + + @Override + public boolean isEmpty() { + return getNotNull().isEmpty(); + } + + @Override + public boolean contains(Object o) { + return getNotNull().contains(o); + } + + @NotNull + @Override + public Iterator iterator() { + return getNotNull().iterator(); + } + + @NotNull + @Override + public Object @NotNull [] toArray() { + return getNotNull().toArray(); + } + + @NotNull + @Override + public T @NotNull [] toArray(@NotNull T[] a) { + return getNotNull().toArray(a); + } + + @Override + public boolean containsAll(@NotNull Collection c) { + return new HashSet<>(getNotNull()).containsAll(c); + } + + @Override + public boolean add(V v) { + handle(list -> list.add(v)); + return true; + } + + @Override + public void add(int index, V element) { + modify(list -> list.add(index, element)); + } + + @Override + public boolean addAll(@NotNull Collection c) { + return handle(list -> list.addAll(c)); + } + + @Override + public boolean addAll(int index, @NotNull Collection c) { + return handle(list -> list.addAll(index, c)); + } + + @Override + public boolean remove(Object o) { + return handle(list -> list.remove(o)); + } + + @Override + public V remove(int index) { + return handle(list -> list.remove(index)); + } + + @Override + public boolean removeAll(@NotNull Collection c) { + return handle(list -> list.removeAll(c)); + } + + @Override + public boolean retainAll(@NotNull Collection c) { + return handle(list -> list.retainAll(c)); + } + + @Override + public void clear() { + modify(List::clear); + } + + @Override + public int indexOf(Object o) { + return getNotNull().indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return getNotNull().lastIndexOf(o); + } + + @NotNull + @Override + public ListIterator listIterator() { + return getNotNull().listIterator(); + } + + @NotNull + @Override + public ListIterator listIterator(int index) { + return getNotNull().listIterator(index); + } + + @NotNull + @Override + public List subList(int fromIndex, int toIndex) { + return getNotNull().subList(fromIndex, toIndex); + } + +} \ No newline at end of file diff --git a/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredMap.java b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredMap.java new file mode 100644 index 0000000..b6137de --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredMap.java @@ -0,0 +1,196 @@ +package cc.carm.lib.configuration.value.standard; + +import cc.carm.lib.configuration.adapter.ValueAdapter; +import cc.carm.lib.configuration.adapter.ValueParser; +import cc.carm.lib.configuration.adapter.ValueSerializer; +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.source.section.ConfigureSection; +import cc.carm.lib.configuration.value.ValueManifest; +import cc.carm.lib.configuration.value.impl.CachedConfigValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; + +public class ConfiguredMap extends CachedConfigValue> implements Map { + + protected final @NotNull Supplier> constructor; + + protected final @NotNull ValueAdapter keyAdapter; + protected final @NotNull ValueAdapter valueAdapter; + + protected ConfiguredMap(@NotNull ValueManifest> manifest, + @NotNull Supplier> constructor, + @NotNull ValueAdapter keyAdapter, @NotNull ValueAdapter valueAdapter) { + super(manifest); + this.constructor = constructor; + this.keyAdapter = keyAdapter; + this.valueAdapter = valueAdapter; + } + + public @NotNull ValueAdapter keyAdapter() { + return keyAdapter; + } + + public @NotNull ValueType keyType() { + return keyAdapter().type(); + } + + public @NotNull ValueAdapter valueAdapter() { + return valueAdapter; + } + + public @NotNull ValueType valueType() { + return valueAdapter().type(); + } + + private Map createMap() { + return this.constructor.get(); + } + + @Override + public @NotNull Map get() { + if (!cacheExpired()) return getCachedOrDefault(createMap()); + // If the value is expired, we need to update it + Map map = createMap(); + + ConfigureSection section = config().getSection(path()); + if (section == null) return getDefaultFirst(map); + + Set keys = section.getKeys(false); + if (keys.isEmpty()) return getDefaultFirst(map); + + ValueParser keyParser = parserFor(keyAdapter); + ValueParser valueParser = parserFor(valueAdapter); + if (keyParser == null || valueParser == null) return getDefaultFirst(map); + + for (String dataKey : keys) { + Object dataVal = section.get(dataKey); + if (dataVal == null) continue; + try { + K key = keyParser.parse(holder(), keyType(), dataKey); + V value = valueParser.parse(holder(), valueType(), dataVal); + map.put(key, value); + } catch (Exception e) { + e.printStackTrace(); + } + } + + return updateCache(map); + } + + @Override + public V get(Object key) { + return get().get(key); + } + + public V getNotNull(Object key) { + return Objects.requireNonNull(get(key)); + } + + @Override + public void set(@Nullable Map value) { + updateCache(value); + if (value == null) { + setData(null); + return; + } + + ValueSerializer keySerializer = serializerFor(keyAdapter); + ValueSerializer valueSerializer = serializerFor(valueAdapter); + if (keySerializer == null || valueSerializer == null) return; + + Map data = new LinkedHashMap<>(); + + for (Map.Entry entry : value.entrySet()) { + try { + data.put( + keySerializer.serialize(holder(), keyType(), entry.getKey()), + valueSerializer.serialize(holder(), valueType(), entry.getValue()) + ); + } catch (Exception e) { + e.printStackTrace(); + } + } + setData(data); + } + + public @NotNull T handle(Function, T> function) { + Map m = get(); + T result = function.apply(m); + set(m); + return result; + } + + public @NotNull ConfiguredMap modify(Consumer> consumer) { + Map m = get(); + consumer.accept(m); + set(m); + return this; + } + + @Override + public int size() { + return get().size(); + } + + @Override + public boolean isEmpty() { + return get().isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return get().containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return get().containsValue(value); + } + + @Nullable + @Override + public V put(K key, V value) { + return handle(m -> m.put(key, value)); + } + + @Override + public V remove(Object key) { + return handle(m -> m.remove(key)); + } + + @Override + public void putAll(@NotNull Map m) { + modify(map -> map.putAll(m)); + } + + @Override + public void clear() { + modify(Map::clear); + } + + @NotNull + @Override + public Set keySet() { + return get().keySet(); + } + + @NotNull + @Override + public Collection values() { + return get().values(); + } + + @NotNull + @Override + @Unmodifiable + public Set> entrySet() { + return get().entrySet(); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredValue.java b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredValue.java new file mode 100644 index 0000000..770d76b --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredValue.java @@ -0,0 +1,143 @@ +package cc.carm.lib.configuration.value.standard; + +import cc.carm.lib.configuration.adapter.ValueAdapter; +import cc.carm.lib.configuration.adapter.ValueParser; +import cc.carm.lib.configuration.adapter.ValueSerializer; +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.builder.value.ConfigValueBuilder; +import cc.carm.lib.configuration.value.ValueManifest; +import cc.carm.lib.configuration.value.impl.CachedConfigValue; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +public class ConfiguredValue extends CachedConfigValue { + + public static ConfigValueBuilder builderOf(@NotNull Class type) { + return new ConfigValueBuilder<>(ValueType.of(type)); + } + + public static ConfigValueBuilder builderOf(@NotNull ValueType type) { + return new ConfigValueBuilder<>(type); + } + + public static ConfiguredValue of(@NotNull V defaults) { + return of(ValueType.of(defaults), () -> defaults); + } + + public static ConfiguredValue of(@NotNull Class type) { + return of(ValueType.of(type), () -> null); + } + + public static ConfiguredValue of(@NotNull Class type, @NotNull V defaults) { + return of(ValueType.of(type), () -> defaults); + } + + + public static ConfiguredValue of(@NotNull Class type, @NotNull Supplier<@Nullable V> defaultSupplier) { + return of(ValueType.of(type), defaultSupplier); + } + + public static ConfiguredValue of(@NotNull ValueType type) { + return of(type, () -> null); + } + + public static ConfiguredValue of(@NotNull ValueType type, @NotNull Supplier<@Nullable V> defaultSupplier) { + return of( + new ValueManifest<>(type, defaultSupplier), + (provider, t, data) -> provider.deserialize(type, data), + (provider, t, value) -> provider.serialize(value) + ); + } + + public static ConfiguredValue of(@NotNull ValueManifest manifest, + @Nullable ValueParser parser, + @Nullable ValueSerializer serializer) { + ValueAdapter adapter = new ValueAdapter<>(manifest.type()); + adapter.parser(parser); + adapter.serializer(serializer); + return of(manifest, adapter); + } + + public static ConfiguredValue of(@NotNull ValueManifest manifest, @NotNull ValueAdapter adapter) { + return new ConfiguredValue<>(manifest, adapter); + } + + protected final @NotNull ValueAdapter adapter; + + public ConfiguredValue(@NotNull ValueManifest manifest, @NotNull ValueAdapter adapter) { + super(manifest); + this.adapter = adapter; + } + + /** + * @return Adapter of this value. + */ + public @NotNull ValueAdapter adapter() { + return adapter; + } + + /** + * @return Value's parser, parse base object to value. + */ + public @Nullable ValueParser parser() { + return parserFor(adapter()); + } + + /** + * @return Value's serializer, parse value to base object. + */ + public @Nullable ValueSerializer serializer() { + return serializerFor(adapter()); + } + + @Override + public V get() { + if (!cacheExpired()) return getCachedOrDefault(); + // Data that is outdated and needs to be parsed again. + + Object data = getData(); + if (data == null) return defaults(); + + ValueParser parser = parser(); + if (parser == null) return defaults(); // No parser, return default value. + + try { + // If there are no errors, update the cache and return. + return updateCache(parser.parse(holder(), type(), data)); + } catch (Exception e) { + // There was a parsing error, prompted and returned the default value. + e.printStackTrace(); + return defaults(); + } + + } + + /** + * Set the value of the configuration path. + * Will use {@link #serializer()} to serialize the value. + * + * @param value The value to be set + */ + @Override + public void set(V value) { + updateCache(value); // Update cache + if (value == null) { + setData(null); + return; + } + + ValueSerializer serializer = serializer(); + if (serializer == null) return; // No serializer, do nothing. + + try { + setData(serializer.serialize(holder(), type(), value)); + } catch (Exception e) { + e.printStackTrace(); + } + + } + +} + diff --git a/core/src/test/java/AdaptTest.java b/core/src/test/java/AdaptTest.java new file mode 100644 index 0000000..bf55bcc --- /dev/null +++ b/core/src/test/java/AdaptTest.java @@ -0,0 +1,57 @@ +import cc.carm.lib.configuration.adapter.ValueAdapterRegistry; +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; +import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder; +import cc.carm.test.config.TestSource; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +import java.time.Duration; +import java.time.LocalTime; +import java.util.concurrent.ConcurrentHashMap; + +public class AdaptTest { + + @Test + public void test() throws Exception { + + ValueAdapterRegistry registry = new ValueAdapterRegistry(); + registry.register(PrimitiveAdapter.ADAPTERS); + registry.register(PrimitiveAdapter.ofEnum()); + + + registry.register(ValueType.of(Long.class), ValueType.of(Duration.class), Duration::ofMillis, Duration::toMillis); + registry.register( + ValueType.of(Duration.class), ValueType.of(LocalTime.class), + duration -> LocalTime.now().plus(duration), + data -> Duration.between(LocalTime.now(), data) + ); + + ConfigurationHolder provider = new ConfigurationHolder( + registry, new ConfigurationOptionHolder(), + new ConcurrentHashMap<>(), new ConfigurationInitializer() + ) { + final TestSource source = new TestSource(this, System.currentTimeMillis()); + + @Override + public @NotNull TestSource config() { + return source; + } + }; + + LocalTime v = registry.deserialize(provider, LocalTime.class, 600000L); + Object d = registry.serialize(provider, v); + + System.out.println(v); + System.out.println(d); + System.out.println(registry.deserialize(provider, TestEnum.class, "C")); + System.out.println(registry.serialize(provider, TestEnum.C).getClass()); + } + + enum TestEnum { + A, b, C + } + +} diff --git a/core/src/test/java/NameTest.java b/core/src/test/java/NameTest.java index e3eaab8..604057c 100644 --- a/core/src/test/java/NameTest.java +++ b/core/src/test/java/NameTest.java @@ -1,4 +1,4 @@ -import cc.carm.lib.configuration.core.ConfigInitializer; +import cc.carm.lib.configuration.source.loader.PathGenerator; import org.junit.Test; public class NameTest { @@ -7,10 +7,10 @@ public class NameTest { @Test public void onTest() { - System.out.println(ConfigInitializer.getPathFromName("LoveGames")); // -> love-games - System.out.println(ConfigInitializer.getPathFromName("EASY_GAME")); // -> easy-game - System.out.println(ConfigInitializer.getPathFromName("F")); //-? f - System.out.println(ConfigInitializer.getPathFromName("Test123123")); // -? test123123123 + System.out.println(PathGenerator.covertPathName("LoveGames")); // -> love-games + System.out.println(PathGenerator.covertPathName("EASY_GAME")); // -> easy-game + System.out.println(PathGenerator.covertPathName("F")); //-? f + System.out.println(PathGenerator.covertPathName("Test123123")); // -? test123123123 } diff --git a/core/src/test/java/cc/carm/test/config/LoaderTest.java b/core/src/test/java/cc/carm/test/config/LoaderTest.java new file mode 100644 index 0000000..fdffa23 --- /dev/null +++ b/core/src/test/java/cc/carm/test/config/LoaderTest.java @@ -0,0 +1,55 @@ +package cc.carm.test.config; + +import cc.carm.lib.configuration.adapter.ValueAdapterRegistry; +import cc.carm.lib.configuration.annotation.ConfigPath; +import cc.carm.lib.configuration.Configuration; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; +import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder; +import org.jetbrains.annotations.NotNull; +import org.junit.Test; + +import java.util.concurrent.ConcurrentHashMap; + +public class LoaderTest { + + @Test + public void test() throws Exception { + ConfigurationHolder provider = new ConfigurationHolder( + new ValueAdapterRegistry(), new ConfigurationOptionHolder(), + new ConcurrentHashMap<>(), new ConfigurationInitializer() + ) { + final TestSource source = new TestSource(this, System.currentTimeMillis()); + + @Override + public @NotNull TestSource config() { + return source; + } + }; + + ConfigurationInitializer loader = new ConfigurationInitializer(); + loader.initialize(provider, ROOT.class); + } + + interface ROOT extends Configuration { + + interface SUB extends Configuration { + + + } + + + @ConfigPath(root = true) + interface EXTERNAL extends Configuration { + + + } + + @ConfigPath("NO") + interface YES extends Configuration { + + + } + + } +} \ No newline at end of file diff --git a/core/src/test/java/cc/carm/test/config/TestSection.java b/core/src/test/java/cc/carm/test/config/TestSection.java new file mode 100644 index 0000000..2293a0e --- /dev/null +++ b/core/src/test/java/cc/carm/test/config/TestSection.java @@ -0,0 +1,57 @@ +package cc.carm.test.config; + +import cc.carm.lib.configuration.source.section.ConfigureSection; +import cc.carm.lib.configuration.source.section.ConfigureSource; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class TestSection implements ConfigureSection { + @Override + public @NotNull ConfigureSource source() { + return null; + } + + @Override + public @NotNull Map getValues(boolean deep) { + return Collections.emptyMap(); + } + + @Override + public void set(@NotNull String path, @Nullable Object value) { + + } + + @Override + public boolean contains(@NotNull String path) { + return false; + } + + @Override + public boolean isList(@NotNull String path) { + return false; + } + + @Override + public @Nullable List getList(@NotNull String path) { + return Collections.emptyList(); + } + + @Override + public boolean isSection(@NotNull String path) { + return false; + } + + @Override + public @Nullable ConfigureSection getSection(@NotNull String path) { + return null; + } + + @Override + public @Nullable Object get(@NotNull String path) { + return null; + } +} diff --git a/core/src/test/java/cc/carm/test/config/TestSource.java b/core/src/test/java/cc/carm/test/config/TestSource.java new file mode 100644 index 0000000..6a1d5e9 --- /dev/null +++ b/core/src/test/java/cc/carm/test/config/TestSource.java @@ -0,0 +1,45 @@ +package cc.carm.test.config; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.section.ConfigureSource; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; + +public class TestSource extends ConfigureSource, TestSource> { + + + public TestSource(@NotNull ConfigurationHolder holder, long lastUpdateMillis) { + super(holder, lastUpdateMillis); + } + + @Override + protected TestSource self() { + return this; + } + + @Override + public void save() throws Exception { + + } + + @Override + protected void onReload() throws Exception { + + } + + @Override + public @NotNull Map original() { + return null; + } + + @Override + public @NotNull TestSection section() { + return null; + } + + @Override + public @NotNull ConfigureSource source() { + return null; + } +} diff --git a/demo/pom.xml b/demo/pom.xml index 5a8324b..13700da 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -26,6 +26,13 @@ compile + + ${project.parent.groupId} + easyconfiguration-feature-commentable + ${project.parent.version} + compile + + diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java b/demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java index 0d93407..f35e99c 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java @@ -1,13 +1,13 @@ package cc.carm.lib.configuration.demo; -import cc.carm.lib.configuration.core.ConfigurationRoot; -import cc.carm.lib.configuration.core.annotation.ConfigPath; -import cc.carm.lib.configuration.core.annotation.HeaderComment; -import cc.carm.lib.configuration.core.value.ConfigValue; -import cc.carm.lib.configuration.core.value.type.ConfiguredValue; +import cc.carm.lib.configuration.Configuration; +import cc.carm.lib.configuration.annotation.ConfigPath; +import cc.carm.lib.configuration.annotation.HeaderComment; +import cc.carm.lib.configuration.value.ConfigValue; +import cc.carm.lib.configuration.value.standard.ConfiguredValue; @HeaderComment({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"}) -public class DatabaseConfiguration extends ConfigurationRoot { +public class DatabaseConfiguration implements Configuration { @ConfigPath("driver") @HeaderComment({ diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/ConfigurationTest.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/ConfigurationTest.java index 10ade90..1f52303 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/ConfigurationTest.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/ConfigurationTest.java @@ -1,12 +1,11 @@ package cc.carm.lib.configuration.demo.tests; -import cc.carm.lib.configuration.core.source.ConfigurationProvider; import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration; -import cc.carm.lib.configuration.demo.tests.conf.TestConfiguration; -import cc.carm.lib.configuration.demo.tests.model.TestModel; +import cc.carm.lib.configuration.demo.tests.conf.RegistryConfig; +import cc.carm.lib.configuration.demo.tests.model.UserRecord; +import cc.carm.lib.configuration.source.ConfigurationHolder; import org.jetbrains.annotations.TestOnly; -import java.util.LinkedHashMap; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -15,8 +14,12 @@ import java.util.stream.IntStream; public class ConfigurationTest { @TestOnly - public static void testDemo(ConfigurationProvider provider) { - provider.initialize(DemoConfiguration.class); + public static void testDemo(ConfigurationHolder holder) { + try { + holder.initialize(DemoConfiguration.class); + } catch (Exception e) { + e.printStackTrace(); + } System.out.println("----------------------------------------------------"); @@ -27,57 +30,57 @@ public class ConfigurationTest { System.out.println("after: " + DemoConfiguration.TEST_NUMBER.get()); System.out.println("> Test Value:"); - System.out.println("before: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get()); - DemoConfiguration.Sub.UUID_CONFIG_VALUE.set(UUID.randomUUID()); - System.out.println("after: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get()); + System.out.println("before: " + DemoConfiguration.SUB.UUID_CONFIG_VALUE.get()); + DemoConfiguration.SUB.UUID_CONFIG_VALUE.set(UUID.randomUUID()); + System.out.println("after: " + DemoConfiguration.SUB.UUID_CONFIG_VALUE.get()); System.out.println("> Test List:"); System.out.println(" Before:"); - DemoConfiguration.Sub.That.OPERATORS.forEach(System.out::println); + DemoConfiguration.SUB.That.OPERATORS.forEach(System.out::println); List operators = IntStream.range(0, 5).mapToObj(i -> UUID.randomUUID()).collect(Collectors.toList()); - DemoConfiguration.Sub.That.OPERATORS.set(operators); + DemoConfiguration.SUB.That.OPERATORS.set(operators); System.out.println(" After:"); - DemoConfiguration.Sub.That.OPERATORS.forEach(System.out::println); + DemoConfiguration.SUB.That.OPERATORS.forEach(System.out::println); System.out.println("> Clear List:"); - System.out.println(" Before: size :" + DemoConfiguration.Sub.That.OPERATORS.size()); - DemoConfiguration.Sub.That.OPERATORS.modify(List::clear); - System.out.println(" After size :" + DemoConfiguration.Sub.That.OPERATORS.size()); + System.out.println(" Before: size :" + DemoConfiguration.SUB.That.OPERATORS.size()); + DemoConfiguration.SUB.That.OPERATORS.modify(List::clear); + System.out.println(" After size :" + DemoConfiguration.SUB.That.OPERATORS.size()); System.out.println("> Test Section:"); - System.out.println(DemoConfiguration.MODEL_TEST.get()); - DemoConfiguration.MODEL_TEST.set(TestModel.random()); + System.out.println(DemoConfiguration.USERS.get()); + DemoConfiguration.USERS.add(UserRecord.random()); - System.out.println("> Test Maps:"); - DemoConfiguration.USERS.forEach((k, v) -> System.out.println(k + ": " + v)); - LinkedHashMap data = new LinkedHashMap<>(); - for (int i = 1; i <= 5; i++) { - data.put(i, UUID.randomUUID()); - } - DemoConfiguration.USERS.set(data); +// System.out.println("> Test Maps:"); +// DemoConfiguration.USERS.forEach((k, v) -> System.out.println(k + ": " + v)); +// LinkedHashMap data = new LinkedHashMap<>(); +// for (int i = 1; i <= 5; i++) { +// data.put(i, UUID.randomUUID()); +// } +// DemoConfiguration.USERS.set(data); System.out.println("----------------------------------------------------"); } - public static void testInner(ConfigurationProvider provider) { + public static void testInner(ConfigurationHolder provider) { - TestConfiguration TEST = new TestConfiguration(); + RegistryConfig TEST = new RegistryConfig(); - provider.initialize(TEST, true); + provider.initialize(TEST); System.out.println("> Test Inner value before:"); - System.out.println(TEST.INNER.INNER_VALUE.getNotNull()); + System.out.println(TEST.INSTANCE.INNER_VALUE.getNotNull()); double after = Math.random() * 200D; System.out.println("> Test Inner value -> " + after); - TEST.INNER.INNER_VALUE.set(after); + TEST.INSTANCE.INNER_VALUE.set(after); System.out.println("> Test Inner value after:"); - System.out.println(TEST.INNER.INNER_VALUE.getNotNull()); + System.out.println(TEST.INSTANCE.INNER_VALUE.getNotNull()); } - public static void save(ConfigurationProvider provider) { + public static void save(ConfigurationHolder provider) { try { provider.save(); } catch (Exception e) { diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java index d288459..8623ac8 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java @@ -1,17 +1,14 @@ package cc.carm.lib.configuration.demo.tests.conf; -import cc.carm.lib.configuration.core.ConfigInitializer; -import cc.carm.lib.configuration.core.Configuration; -import cc.carm.lib.configuration.core.ConfigurationRoot; -import cc.carm.lib.configuration.core.annotation.ConfigPath; -import cc.carm.lib.configuration.core.annotation.HeaderComment; -import cc.carm.lib.configuration.core.annotation.InlineComment; -import cc.carm.lib.configuration.core.value.ConfigValue; -import cc.carm.lib.configuration.core.value.type.ConfiguredList; -import cc.carm.lib.configuration.core.value.type.ConfiguredMap; -import cc.carm.lib.configuration.core.value.type.ConfiguredSection; -import cc.carm.lib.configuration.core.value.type.ConfiguredValue; -import cc.carm.lib.configuration.demo.tests.model.TestModel; +import cc.carm.lib.configuration.Configuration; +import cc.carm.lib.configuration.annotation.ConfigPath; +import cc.carm.lib.configuration.annotation.HeaderComment; +import cc.carm.lib.configuration.annotation.InlineComment; +import cc.carm.lib.configuration.demo.DatabaseConfiguration; +import cc.carm.lib.configuration.demo.tests.model.UserRecord; +import cc.carm.lib.configuration.value.ConfigValue; +import cc.carm.lib.configuration.value.standard.ConfiguredList; +import cc.carm.lib.configuration.value.standard.ConfiguredValue; import java.time.temporal.ChronoUnit; import java.util.Objects; @@ -31,45 +28,41 @@ public interface DemoConfiguration extends Configuration { // 支持通过 Class 变量标注子配置,一并注册。 // 注意: 若对应类也有注解,则优先使用类的注解。 - @ConfigPath("other-class-config") //支持通过注解修改子配置的主路径,若不修改则以变量名自动生成。 - @HeaderComment({"", "Something..."}) // 支持给子路径直接打注释 - @InlineComment("InlineComments for class path") - Class OTHER = OtherConfiguration.class; + Class DATABASE = DatabaseConfiguration.class; - @ConfigPath("user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。 + @ConfigPath("registered_users") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。 @HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。 @InlineComment("Section数据也支持InlineComment注释") - ConfigValue MODEL_TEST = ConfiguredSection.builderOf(TestModel.class) - .defaults(new TestModel("Carm", UUID.randomUUID())) - .parseValue((section, defaultValue) -> TestModel.deserialize(section)) - .serializeValue(TestModel::serialize).build(); + ConfiguredList USERS = ConfiguredList.builderOf(UserRecord.class).fromSection() + .parse(UserRecord::deserialize).serialize(UserRecord::serialize) + .defaults(UserRecord.CARM).build(); - @HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"}) - ConfiguredMap USERS = ConfiguredMap.builderOf(Integer.class, UUID.class) - .asLinkedMap().fromString() - .parseKey(Integer::parseInt) - .parseValue(v -> Objects.requireNonNull(UUID.fromString(v))) - .build(); +// @HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"}) +// ConfiguredMap USERS = ConfiguredMap.builderOf(Integer.class, UUID.class) +// .asLinkedMap().fromString() +// .parseKey(Integer::parseInt) +// .parseValue(v -> Objects.requireNonNull(UUID.fromString(v))) +// .build(); /** * 支持内部类的直接注册。 - * 注意,需要使用 {@link ConfigInitializer#initialize(Class, boolean, boolean)} 方法,并设定第三个参数为 true。 + * 注意,需要启用 {@link cc.carm.lib.configuration.source.option.StandardOptions#LOAD_SUB_CLASSES} */ - class Sub extends ConfigurationRoot { + class SUB implements Configuration { @ConfigPath(value = "uuid-value", root = true) @InlineComment("This is an inline comment") public static final ConfigValue UUID_CONFIG_VALUE = ConfiguredValue .builderOf(UUID.class).fromString() - .parseValue((data, defaultValue) -> UUID.fromString(data)) + .parse((holder, data) -> UUID.fromString(data)) .build(); - public static class That extends ConfigurationRoot { + public static class That implements Configuration { public static final ConfiguredList OPERATORS = ConfiguredList .builderOf(UUID.class).fromString() - .parseValue(s -> Objects.requireNonNull(UUID.fromString(s))) + .parse(s -> Objects.requireNonNull(UUID.fromString(s))) .build(); } diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/InstanceConfig.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/InstanceConfig.java new file mode 100644 index 0000000..c0d7a8b --- /dev/null +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/InstanceConfig.java @@ -0,0 +1,13 @@ +package cc.carm.lib.configuration.demo.tests.conf; + +import cc.carm.lib.configuration.Configuration; +import cc.carm.lib.configuration.annotation.HeaderComment; +import cc.carm.lib.configuration.value.ConfigValue; +import cc.carm.lib.configuration.value.standard.ConfiguredValue; + +@HeaderComment("Inner Test") +public class InstanceConfig implements Configuration { + + public final ConfigValue INNER_VALUE = ConfiguredValue.of(1.0D); + +} diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/OtherConfiguration.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/OtherConfiguration.java deleted file mode 100644 index cb0fd2c..0000000 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/OtherConfiguration.java +++ /dev/null @@ -1,4 +0,0 @@ -package cc.carm.lib.configuration.demo.tests.conf; - -public class OtherConfiguration { -} diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/RegistryConfig.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/RegistryConfig.java new file mode 100644 index 0000000..36fceca --- /dev/null +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/RegistryConfig.java @@ -0,0 +1,27 @@ +package cc.carm.lib.configuration.demo.tests.conf; + +import cc.carm.lib.configuration.Configuration; +import cc.carm.lib.configuration.annotation.ConfigPath; +import cc.carm.lib.configuration.annotation.HeaderComment; +import cc.carm.lib.configuration.annotation.InlineComment; +import cc.carm.lib.configuration.demo.tests.model.UserRecord; +import cc.carm.lib.configuration.value.ConfigValue; +import cc.carm.lib.configuration.value.standard.ConfiguredValue; + +import java.util.UUID; + +public class RegistryConfig implements Configuration { + + @HeaderComment("Support for configurations as instances") + public final InstanceConfig INSTANCE = new InstanceConfig(); + + @ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。 + @HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。 + @InlineComment("Section数据也支持InlineComment注释") + public final ConfigValue TEST_MODEL = ConfiguredValue.builderOf(UserRecord.class).fromSection() + .defaults(new UserRecord("Carm", UUID.randomUUID())) + .parse((holder, section) -> UserRecord.deserialize(section)) + .serialize((holder, data) -> data.serialize()).build(); + + +} diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/TestConfiguration.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/TestConfiguration.java deleted file mode 100644 index e9a3257..0000000 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/TestConfiguration.java +++ /dev/null @@ -1,30 +0,0 @@ -package cc.carm.lib.configuration.demo.tests.conf; - -import cc.carm.lib.configuration.core.ConfigurationRoot; -import cc.carm.lib.configuration.core.annotation.ConfigPath; -import cc.carm.lib.configuration.core.annotation.HeaderComment; -import cc.carm.lib.configuration.core.annotation.InlineComment; -import cc.carm.lib.configuration.core.value.ConfigValue; -import cc.carm.lib.configuration.core.value.type.ConfiguredSection; -import cc.carm.lib.configuration.core.value.type.ConfiguredValue; -import cc.carm.lib.configuration.demo.tests.model.TestModel; - -import java.util.UUID; - -public class TestConfiguration extends ConfigurationRoot { - - public final TestInnerConfiguration INNER = new TestInnerConfiguration(); - - public final ConfigValue CLASS_VALUE = ConfiguredValue.of(Double.class, 1.0D); - - @ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。 - @HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。 - @InlineComment("Section数据也支持InlineComment注释") - public final ConfigValue TEST_MODEL = ConfiguredSection - .builderOf(TestModel.class) - .defaults(new TestModel("Carm", UUID.randomUUID())) - .parseValue((section, defaultValue) -> TestModel.deserialize(section)) - .serializeValue(TestModel::serialize).build(); - - -} diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/TestInnerConfiguration.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/TestInnerConfiguration.java deleted file mode 100644 index 8385a6a..0000000 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/TestInnerConfiguration.java +++ /dev/null @@ -1,13 +0,0 @@ -package cc.carm.lib.configuration.demo.tests.conf; - -import cc.carm.lib.configuration.core.ConfigurationRoot; -import cc.carm.lib.configuration.core.annotation.HeaderComment; -import cc.carm.lib.configuration.core.value.ConfigValue; -import cc.carm.lib.configuration.core.value.type.ConfiguredValue; - -@HeaderComment("Inner Test") -public class TestInnerConfiguration extends ConfigurationRoot { - - public final ConfigValue INNER_VALUE = ConfiguredValue.of(Double.class, 1.0D); - -} diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/AbstractModel.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/AbstractRecord.java similarity index 73% rename from demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/AbstractModel.java rename to demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/AbstractRecord.java index f12e411..d8e09ce 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/AbstractModel.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/AbstractRecord.java @@ -2,11 +2,11 @@ package cc.carm.lib.configuration.demo.tests.model; import org.jetbrains.annotations.NotNull; -public abstract class AbstractModel { +public abstract class AbstractRecord { protected final @NotNull String name; - public AbstractModel(@NotNull String name) { + public AbstractRecord(@NotNull String name) { this.name = name; } diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/TestModel.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/UserRecord.java similarity index 61% rename from demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/TestModel.java rename to demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/UserRecord.java index 9e53d95..f14240a 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/TestModel.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/model/UserRecord.java @@ -1,26 +1,28 @@ package cc.carm.lib.configuration.demo.tests.model; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; +import cc.carm.lib.configuration.source.section.ConfigureSection; import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.Map; import java.util.UUID; -public class TestModel extends AbstractModel { +public class UserRecord extends AbstractRecord { - public UUID uuid; + public static final UserRecord CARM = new UserRecord("Carm", UUID.fromString("f7b3b3b3-3b3b-3b3b-3b3b-3b3b3b3b3b3b")); - public TestModel(String name, UUID uuid) { + protected UUID uuid; + + public UserRecord(String name, UUID uuid) { super(name); this.uuid = uuid; } - public void setUuid(UUID uuid) { + public void uuid(UUID uuid) { this.uuid = uuid; } - public UUID getUuid() { + public UUID uuid() { return uuid; } @@ -33,16 +35,16 @@ public class TestModel extends AbstractModel { return map; } - public static TestModel deserialize(ConfigurationWrapper section) { + public static UserRecord deserialize(ConfigureSection section) { String name = section.getString("name"); if (name == null) throw new NullPointerException("name is null"); String uuidString = section.getString("info.uuid"); if (uuidString == null) throw new NullPointerException("uuid is null"); - return new TestModel(name, UUID.fromString(uuidString)); + return new UserRecord(name, UUID.fromString(uuidString)); } - public static TestModel random() { - return new TestModel(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID()); + public static UserRecord random() { + return new UserRecord(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID()); } diff --git a/features/commentable/pom.xml b/features/commentable/pom.xml new file mode 100644 index 0000000..5aee317 --- /dev/null +++ b/features/commentable/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + cc.carm.lib + easyconfiguration-parent + 3.9.1 + ../../pom.xml + + + ${project.jdk.version} + ${project.jdk.version} + UTF-8 + UTF-8 + + + easyconfiguration-feature-commentable + jar + + + + ${project.groupId} + easyconfiguration-core + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + \ No newline at end of file diff --git a/features/commentable/src/main/java/cc/carm/lib/configuration/annotation/FooterComment.java b/features/commentable/src/main/java/cc/carm/lib/configuration/annotation/FooterComment.java new file mode 100644 index 0000000..24971d6 --- /dev/null +++ b/features/commentable/src/main/java/cc/carm/lib/configuration/annotation/FooterComment.java @@ -0,0 +1,40 @@ +package cc.carm.lib.configuration.annotation; + +import org.jetbrains.annotations.NotNull; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Footer Comments. + * Add a comment to the bottom of the corresponding configuration for easy reading and viewing. + *

e.g. + *

+ * foo: "bar"
+ * # The first line of the comment
+ * # The second line of the comment
+ * 
+ */ +@Target({ElementType.TYPE, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface FooterComment { + + /** + * If the content of the note is 0, it will be treated as a blank line. + *

e.g. {"foo","","bar"} + * Will be set as + *

+     * foo: "bar"
+     * # foo
+     *
+     * # bar
+     * 
+ * + * @return The content of this comment + */ + @NotNull + String[] value() default ""; + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/annotation/HeaderComment.java b/features/commentable/src/main/java/cc/carm/lib/configuration/annotation/HeaderComment.java similarity index 94% rename from core/src/main/java/cc/carm/lib/configuration/core/annotation/HeaderComment.java rename to features/commentable/src/main/java/cc/carm/lib/configuration/annotation/HeaderComment.java index 2089a46..284b290 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/annotation/HeaderComment.java +++ b/features/commentable/src/main/java/cc/carm/lib/configuration/annotation/HeaderComment.java @@ -1,4 +1,4 @@ -package cc.carm.lib.configuration.core.annotation; +package cc.carm.lib.configuration.annotation; import org.jetbrains.annotations.NotNull; diff --git a/core/src/main/java/cc/carm/lib/configuration/core/annotation/InlineComment.java b/features/commentable/src/main/java/cc/carm/lib/configuration/annotation/InlineComment.java similarity index 54% rename from core/src/main/java/cc/carm/lib/configuration/core/annotation/InlineComment.java rename to features/commentable/src/main/java/cc/carm/lib/configuration/annotation/InlineComment.java index 1d3a750..763f896 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/annotation/InlineComment.java +++ b/features/commentable/src/main/java/cc/carm/lib/configuration/annotation/InlineComment.java @@ -1,4 +1,4 @@ -package cc.carm.lib.configuration.core.annotation; +package cc.carm.lib.configuration.annotation; import org.jetbrains.annotations.NotNull; @@ -31,4 +31,23 @@ public @interface InlineComment { @NotNull String value() default ""; + /** + * If the regex is not empty, the comment will be added to + * all sub paths if the regex matches the value. + * If the regex is empty, the comment will be added to the current path. + *

e.g. "^foo\\.*\\.bar" will be set like + *

+     *     foo:
+     *       some:
+     *         lover: "bar" <- not matched so no comments
+     *         bar: "foobar" # Comment Contents
+     *       other:
+     *         bar: "foobar" # Comment Contents
+     * 
+ * + * @return The path regexes of this comment + */ + @NotNull + String[] regex() default {}; + } diff --git a/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/CommentableMetaTypes.java b/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/CommentableMetaTypes.java new file mode 100644 index 0000000..ce73428 --- /dev/null +++ b/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/CommentableMetaTypes.java @@ -0,0 +1,49 @@ +package cc.carm.lib.configuration.commentable; + +import cc.carm.lib.configuration.annotation.FooterComment; +import cc.carm.lib.configuration.annotation.HeaderComment; +import cc.carm.lib.configuration.annotation.InlineComment; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; +import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public interface CommentableMetaTypes { + + /** + * Configuration's {@link HeaderComment} + */ + ConfigurationMetadata> HEADER_COMMENTS = ConfigurationMetadata.of(Collections.emptyList()); + + /** + * Configuration's footer comments + */ + ConfigurationMetadata> FOOTER_COMMENTS = ConfigurationMetadata.of(Collections.emptyList()); + + /** + * Configuration's {@link InlineComment} + */ + ConfigurationMetadata INLINE_COMMENT = ConfigurationMetadata.of(); + + + static void register(@NotNull ConfigurationHolder provider) { + register(provider.initializer()); + } + + static void register(@NotNull ConfigurationInitializer initializer) { + initializer.registerAnnotation( + HeaderComment.class, HEADER_COMMENTS, + a -> Arrays.asList(a.value()) + ); + initializer.registerAnnotation( + FooterComment.class, FOOTER_COMMENTS, + a -> Arrays.asList(a.value()) + ); + initializer.registerAnnotation(InlineComment.class, INLINE_COMMENT, InlineComment::value); + } + +} diff --git a/features/commentable/src/main/java/cc/carm/lib/configuration/option/CommentableOptions.java b/features/commentable/src/main/java/cc/carm/lib/configuration/option/CommentableOptions.java new file mode 100644 index 0000000..09a89e4 --- /dev/null +++ b/features/commentable/src/main/java/cc/carm/lib/configuration/option/CommentableOptions.java @@ -0,0 +1,24 @@ +package cc.carm.lib.configuration.option; + +import cc.carm.lib.configuration.source.option.ConfigurationOption; + +public interface CommentableOptions { + +// /** +// * Whether to keep modified comments in configuration, +// * that means we only set comments for values that are not exists in configuration. +// */ +// ConfigurationOption KEEP_COMMENTS = ConfigurationOption.of(true); + + /** + * Whether to comment values name that are not exists in configuration and no default value offered. + *
If true, a value without default value is not exists in configuration, we will comment its name, + *

e.g. a value named "foo" without default value will be put as: + *

+     * # Value comments
+     * # foo:
+     * 
+ */ + ConfigurationOption COMMENT_EMPTY_VALUE = ConfigurationOption.of(true); + +} diff --git a/features/file/pom.xml b/features/file/pom.xml new file mode 100644 index 0000000..861bcb1 --- /dev/null +++ b/features/file/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + cc.carm.lib + easyconfiguration-parent + 3.9.1 + ../../pom.xml + + + ${project.jdk.version} + ${project.jdk.version} + UTF-8 + UTF-8 + + + easyconfiguration-feature-file + jar + + + + ${project.groupId} + easyconfiguration-core + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + \ No newline at end of file diff --git a/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigFactory.java b/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigFactory.java new file mode 100644 index 0000000..d112eb2 --- /dev/null +++ b/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigFactory.java @@ -0,0 +1,41 @@ +package cc.carm.lib.configuration.source.file; + +import cc.carm.lib.configuration.source.ConfigurationFactory; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.nio.file.Path; + +public abstract class FileConfigFactory, + HOLDER extends ConfigurationHolder, SELF extends FileConfigFactory> + extends ConfigurationFactory { + + + protected @NotNull File file; + protected @Nullable String resourcePath; + + public FileConfigFactory(@NotNull File file) { + this.file = file; + } + + public SELF file(@NotNull File file) { + this.file = file; + return self(); + } + + public SELF file(@NotNull Path path) { + return file(path.toFile()); + } + + public SELF file(@NotNull File parent, @NotNull String fileName) { + return file(new File(parent, fileName)); + } + + public SELF resourcePath(@Nullable String resourcePath) { + this.resourcePath = resourcePath; + return self(); + } + +} diff --git a/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigSource.java b/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigSource.java new file mode 100644 index 0000000..85b7013 --- /dev/null +++ b/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigSource.java @@ -0,0 +1,132 @@ +package cc.carm.lib.configuration.source.file; + +import cc.carm.lib.configuration.function.DataFunction; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.option.FileConfigOptions; +import cc.carm.lib.configuration.source.section.ConfigureSection; +import cc.carm.lib.configuration.source.section.ConfigureSource; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.Objects; +import java.util.function.Consumer; + +public abstract class FileConfigSource
> + extends ConfigureSource { + + protected final @NotNull File file; + protected final @Nullable String resourcePath; + + protected FileConfigSource(@NotNull ConfigurationHolder holder, long lastUpdateMillis, + @NotNull File file, @Nullable String resourcePath) { + super(holder, lastUpdateMillis); + this.file = file; + this.resourcePath = resourcePath; + } + + + public Charset charset() { + return holder().options().get(FileConfigOptions.CHARSET); + } + + public boolean copyDefaults() { + return holder().options().get(FileConfigOptions.COPY_DEFAULTS); + } + + + public void initializeFile() throws IOException { + if (this.file.exists()) return; + + File parent = this.file.getParentFile(); + if (parent != null && !parent.exists() && !parent.mkdirs()) { + throw new IOException("Failed to create directory " + file.getParentFile().getAbsolutePath()); + } + + if (resourcePath != null && copyDefaults()) { + try { + saveResource(resourcePath, false); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } + + if (!this.file.exists() && !this.file.createNewFile()) { + throw new IOException("Failed to create file " + file.getAbsolutePath()); + } + } + + protected R fileInputStream(@NotNull DataFunction loader) throws Exception { + try (InputStream is = Files.newInputStream(file.toPath())) { + return loader.handle(is); + } + } + + protected R fileReader(@NotNull DataFunction loader) throws Exception { + try (InputStream is = Files.newInputStream(file.toPath())) { + try (Reader r = new InputStreamReader(is, charset())) { + return loader.handle(r); + } + } + } + + protected void fileOutputStream(@NotNull Consumer stream) throws Exception { + try (OutputStream os = Files.newOutputStream(file.toPath())) { + stream.accept(os); + } + } + + protected void fileWriter(@NotNull Consumer writer) throws Exception { + try (OutputStream os = Files.newOutputStream(file.toPath())) { + try (Writer w = new OutputStreamWriter(os, charset())) { + writer.accept(w); + } + } + } + + protected void saveResource(@NotNull String resourcePath, boolean replace) + throws IOException, IllegalArgumentException { + Objects.requireNonNull(resourcePath, "ResourcePath cannot be null"); + if (resourcePath.isEmpty()) throw new IllegalArgumentException("ResourcePath cannot be empty"); + + resourcePath = resourcePath.replace('\\', '/'); + + URL url = this.getClass().getClassLoader().getResource(resourcePath); + if (url == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists"); + + File outDir = file.getParentFile(); + + if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir); + if (!file.exists() || replace) { + try (OutputStream out = Files.newOutputStream(file.toPath())) { + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + try (InputStream in = connection.getInputStream()) { + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + } + } + } + } + + @Nullable + protected InputStream getResource(@NotNull String filename) { + try { + URL url = this.getClass().getClassLoader().getResource(filename); + if (url == null) return null; + URLConnection connection = url.openConnection(); + connection.setUseCaches(false); + return connection.getInputStream(); + } catch (IOException ex) { + return null; + } + } + +} diff --git a/features/file/src/main/java/cc/carm/lib/configuration/source/option/FileConfigOptions.java b/features/file/src/main/java/cc/carm/lib/configuration/source/option/FileConfigOptions.java new file mode 100644 index 0000000..2c46761 --- /dev/null +++ b/features/file/src/main/java/cc/carm/lib/configuration/source/option/FileConfigOptions.java @@ -0,0 +1,20 @@ +package cc.carm.lib.configuration.source.option; + +import cc.carm.lib.configuration.source.option.ConfigurationOption; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; + +public interface FileConfigOptions { + + /** + * The charset of the file. + */ + ConfigurationOption CHARSET = ConfigurationOption.of(StandardCharsets.UTF_8); + + /** + * Whether to copy files from resource if exists. + */ + ConfigurationOption COPY_DEFAULTS = ConfigurationOption.of(true); + +} diff --git a/features/versioned/pom.xml b/features/versioned/pom.xml new file mode 100644 index 0000000..1b40382 --- /dev/null +++ b/features/versioned/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + cc.carm.lib + easyconfiguration-parent + 3.9.1 + ../../pom.xml + + + ${project.jdk.version} + ${project.jdk.version} + UTF-8 + UTF-8 + + + easyconfiguration-feature-versioned + jar + + + + ${project.groupId} + easyconfiguration-core + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + \ No newline at end of file diff --git a/features/versioned/src/main/java/cc/carm/lib/configuration/annotation/ConfigVersion.java b/features/versioned/src/main/java/cc/carm/lib/configuration/annotation/ConfigVersion.java new file mode 100644 index 0000000..19ba260 --- /dev/null +++ b/features/versioned/src/main/java/cc/carm/lib/configuration/annotation/ConfigVersion.java @@ -0,0 +1,28 @@ +package cc.carm.lib.configuration.annotation; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Range; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + +/** + * The version of specific {@link cc.carm.lib.configuration.value.ConfigValue}. + *
Used for versioning target field for rewrite/upgrade. + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ConfigVersion { + + /** + * The version of the configuration field. + * + * @return the version of the configuration field + */ + @Range(from = 0, to = Integer.MAX_VALUE) + int value() default 0; + +} diff --git a/features/versioned/src/main/java/cc/carm/lib/configuration/commentable/VersionedMetaTypes.java b/features/versioned/src/main/java/cc/carm/lib/configuration/commentable/VersionedMetaTypes.java new file mode 100644 index 0000000..5b77346 --- /dev/null +++ b/features/versioned/src/main/java/cc/carm/lib/configuration/commentable/VersionedMetaTypes.java @@ -0,0 +1,25 @@ +package cc.carm.lib.configuration.commentable; + +import cc.carm.lib.configuration.annotation.ConfigVersion; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; +import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; +import org.jetbrains.annotations.NotNull; + +public interface VersionedMetaTypes { + + /** + * The version of specific {@link cc.carm.lib.configuration.value.ConfigValue}. + *
Used for versioning target field for rewrite/upgrade. + */ + ConfigurationMetadata VERSION = ConfigurationMetadata.of(0); + + static void register(@NotNull ConfigurationHolder provider) { + register(provider.initializer()); + } + + static void register(@NotNull ConfigurationInitializer initializer) { + initializer.registerAnnotation(ConfigVersion.class, VERSION, ConfigVersion::value); + } + +} diff --git a/features/versioned/src/main/java/cc/carm/lib/configuration/option/VersionedOptions.java b/features/versioned/src/main/java/cc/carm/lib/configuration/option/VersionedOptions.java new file mode 100644 index 0000000..4387206 --- /dev/null +++ b/features/versioned/src/main/java/cc/carm/lib/configuration/option/VersionedOptions.java @@ -0,0 +1,13 @@ +package cc.carm.lib.configuration.option; + +import cc.carm.lib.configuration.source.option.ConfigurationOption; + +public interface VersionedOptions { + + /** + * Whether to set newer defaults when a {@link cc.carm.lib.configuration.value.ConfigValue}'s marked version + * is newer than the current in storage. + */ + ConfigurationOption RESET_NEWER_DEFAULTS = ConfigurationOption.of(true); + +} diff --git a/impl/json/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java b/impl/json/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java deleted file mode 100644 index 594ff50..0000000 --- a/impl/json/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java +++ /dev/null @@ -1,36 +0,0 @@ -package cc.carm.lib.configuration; - -import cc.carm.lib.configuration.json.JSONConfigProvider; - -import java.io.File; -import java.io.IOException; - -public class EasyConfiguration { - - private EasyConfiguration() { - } - - public static JSONConfigProvider from(File file, String source) { - JSONConfigProvider provider = new JSONConfigProvider(file); - try { - provider.initializeFile(source); - provider.initializeConfig(); - } catch (IOException e) { - e.printStackTrace(); - } - return provider; - } - - public static JSONConfigProvider from(File file) { - return from(file, file.getName()); - } - - public static JSONConfigProvider from(String fileName) { - return from(fileName, fileName); - } - - public static JSONConfigProvider from(String fileName, String source) { - return from(new File(fileName), source); - } - -} diff --git a/impl/json/src/main/java/cc/carm/lib/configuration/json/JSONConfigProvider.java b/impl/json/src/main/java/cc/carm/lib/configuration/json/JSONConfigProvider.java deleted file mode 100644 index f9df64e..0000000 --- a/impl/json/src/main/java/cc/carm/lib/configuration/json/JSONConfigProvider.java +++ /dev/null @@ -1,81 +0,0 @@ -package cc.carm.lib.configuration.json; - -import cc.carm.lib.configuration.core.ConfigInitializer; -import cc.carm.lib.configuration.core.source.ConfigurationComments; -import cc.carm.lib.configuration.core.source.ConfigurationProvider; -import cc.carm.lib.configuration.core.source.impl.FileConfigProvider; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonSerializer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.util.LinkedHashMap; - -/** - * Some code comes from BungeeCord's implementation of the JsonConfiguration. - * - * @author md_5, CarmJos - */ -public class JSONConfigProvider extends FileConfigProvider { - - protected final Gson gson = new GsonBuilder() - .serializeNulls().disableHtmlEscaping().setPrettyPrinting() - .registerTypeAdapter( - JSONConfigWrapper.class, - (JsonSerializer) (src, typeOfSrc, context) -> context.serialize(src.data) - ).create(); - - protected JSONConfigWrapper configuration; - protected ConfigInitializer initializer; - - public JSONConfigProvider(@NotNull File file) { - super(file); - this.initializer = new ConfigInitializer<>(this); - } - - public void initializeConfig() { - onReload(); - } - - @Override - public @NotNull JSONConfigWrapper getConfiguration() { - return this.configuration; - } - - @Override - protected void onReload() { - LinkedHashMap map = null; - - try (FileInputStream is = new FileInputStream(file)) { - map = gson.fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), LinkedHashMap.class); - } catch (IOException e) { - e.printStackTrace(); - } - - if (map == null) map = new LinkedHashMap<>(); - - this.configuration = new JSONConfigWrapper(map); - } - - @Override - public @Nullable ConfigurationComments getComments() { - return null; - } - - @Override - public void save() throws Exception { - try (Writer writer = new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8)) { - gson.toJson(configuration.data, writer); - } - } - - @Override - public @NotNull ConfigInitializer> getInitializer() { - return this.initializer; - } - -} diff --git a/impl/json/src/main/java/cc/carm/lib/configuration/json/JSONConfigWrapper.java b/impl/json/src/main/java/cc/carm/lib/configuration/json/JSONConfigWrapper.java deleted file mode 100644 index c175c2d..0000000 --- a/impl/json/src/main/java/cc/carm/lib/configuration/json/JSONConfigWrapper.java +++ /dev/null @@ -1,137 +0,0 @@ -package cc.carm.lib.configuration.json; - -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Some code comes from BungeeCord's implementation of the JsonConfiguration. - * - * @author md_5, CarmJos - */ -public class JSONConfigWrapper implements ConfigurationWrapper> { - - private static final char SEPARATOR = '.'; - protected final Map data; - - JSONConfigWrapper(Map map) { - this.data = new LinkedHashMap<>(); - - for (Map.Entry entry : map.entrySet()) { - String key = (entry.getKey() == null) ? "null" : entry.getKey().toString(); - - if (entry.getValue() instanceof Map) { - this.data.put(key, new JSONConfigWrapper((Map) entry.getValue())); - } else { - this.data.put(key, entry.getValue()); - } - } - } - - @Override - public @NotNull Map getSource() { - return this.data; - } - - @Override - public @NotNull Set getKeys(boolean deep) { - return getValues(deep).keySet(); - } - - @Override - public @NotNull Map getValues(boolean deep) { - if (deep) { - Map values = new LinkedHashMap<>(); - mapChildrenValues(values, this, null, true); - return values; - } else return new LinkedHashMap<>(this.data); - } - - @Override - public void set(@NotNull String path, @Nullable Object value) { - if (value instanceof Map) { - value = new JSONConfigWrapper((Map) value); - } - - JSONConfigWrapper section = getSectionFor(path); - if (section == this) { - if (value == null) { - this.data.remove(path); - } else { - this.data.put(path, value); - } - } else { - section.set(getChild(path), value); - } - } - - @Override - public boolean contains(@NotNull String path) { - return get(path) != null; - } - - @Override - public @Nullable Object get(@NotNull String path) { - JSONConfigWrapper section = getSectionFor(path); - return section == this ? data.get(path) : section.get(getChild(path)); - } - - @Override - public boolean isList(@NotNull String path) { - return get(path) instanceof List; - } - - @Override - public @Nullable List getList(@NotNull String path) { - Object val = get(path); - return (val instanceof List) ? (List) val : null; - } - - @Override - public boolean isConfigurationSection(@NotNull String path) { - return get(path) instanceof JSONConfigWrapper; - } - - @Override - public @Nullable JSONConfigWrapper getConfigurationSection(@NotNull String path) { - Object val = get(path); - return (val instanceof JSONConfigWrapper) ? (JSONConfigWrapper) val : null; - } - - private JSONConfigWrapper getSectionFor(String path) { - int index = path.indexOf(SEPARATOR); - if (index == -1) return this; - - String root = path.substring(0, index); - Object section = this.data.get(root); - if (section == null) { - section = new JSONConfigWrapper(new LinkedHashMap<>()); - this.data.put(root, section); - } - - return (JSONConfigWrapper) section; - } - - private String getChild(String path) { - int index = path.indexOf(SEPARATOR); - return (index == -1) ? path : path.substring(index + 1); - } - - - protected void mapChildrenValues(@NotNull Map output, @NotNull JSONConfigWrapper section, - @Nullable String parent, boolean deep) { - for (Map.Entry entry : section.data.entrySet()) { - String path = (parent == null ? "" : parent + ".") + entry.getKey(); - output.remove(path); - output.put(path, entry.getValue()); - if (deep && entry.getValue() instanceof JSONConfigWrapper) { - this.mapChildrenValues(output, (JSONConfigWrapper) entry.getValue(), path, true); - } - } - } -} diff --git a/impl/json/src/test/java/config/JSONConfigTest.java b/impl/json/src/test/java/config/JSONConfigTest.java deleted file mode 100644 index 5980c3c..0000000 --- a/impl/json/src/test/java/config/JSONConfigTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package config; - -import cc.carm.lib.configuration.EasyConfiguration; -import cc.carm.lib.configuration.demo.tests.ConfigurationTest; -import cc.carm.lib.configuration.json.JSONConfigProvider; -import org.junit.Test; - -public class JSONConfigTest { - - protected final JSONConfigProvider provider = EasyConfiguration.from("target/config.json", "config.json"); - - - @Test - public void onTest() { - - ConfigurationTest.testDemo(this.provider); - ConfigurationTest.testInner(this.provider); - - System.out.println("----------------------------------------------------"); - provider.getConfiguration().getValues(true).forEach((k, v) -> System.out.println(k + ": " + v)); - System.out.println("----------------------------------------------------"); - provider.getConfiguration().getValues(false).forEach((k, v) -> System.out.println(k + ": " + v)); - System.out.println("----------------------------------------------------"); - - ConfigurationTest.save(this.provider); - } - - -} diff --git a/impl/yaml/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java b/impl/yaml/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java deleted file mode 100644 index aa6aa78..0000000 --- a/impl/yaml/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java +++ /dev/null @@ -1,36 +0,0 @@ -package cc.carm.lib.configuration; - -import cc.carm.lib.configuration.yaml.YAMLConfigProvider; - -import java.io.File; -import java.io.IOException; - -public class EasyConfiguration { - - private EasyConfiguration() { - } - - public static YAMLConfigProvider from(File file, String source) { - YAMLConfigProvider provider = new YAMLConfigProvider(file); - try { - provider.initializeFile(source); - provider.initializeConfig(); - } catch (IOException e) { - e.printStackTrace(); - } - return provider; - } - - public static YAMLConfigProvider from(File file) { - return from(file, file.getName()); - } - - public static YAMLConfigProvider from(String fileName) { - return from(fileName, fileName); - } - - public static YAMLConfigProvider from(String fileName, String source) { - return from(new File(fileName), source); - } - -} diff --git a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/YAMLConfigProvider.java b/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/YAMLConfigProvider.java deleted file mode 100644 index d69b3aa..0000000 --- a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/YAMLConfigProvider.java +++ /dev/null @@ -1,90 +0,0 @@ -package cc.carm.lib.configuration.yaml; - -import cc.carm.lib.configuration.core.ConfigInitializer; -import cc.carm.lib.configuration.core.source.ConfigurationComments; -import cc.carm.lib.configuration.core.source.impl.FileConfigProvider; -import cc.carm.lib.yamlcommentupdater.CommentedYAML; -import cc.carm.lib.yamlcommentupdater.CommentedYAMLWriter; -import org.bspfsystems.yamlconfiguration.configuration.ConfigurationSection; -import org.bspfsystems.yamlconfiguration.file.FileConfiguration; -import org.bspfsystems.yamlconfiguration.file.YamlConfiguration; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.util.List; -import java.util.Set; - -public class YAMLConfigProvider extends FileConfigProvider implements CommentedYAML { - - protected final @NotNull ConfigurationComments comments = new ConfigurationComments(); - protected YamlConfiguration configuration; - protected ConfigInitializer initializer; - - public YAMLConfigProvider(@NotNull File file) { - super(file); - } - - public void initializeConfig() { - this.configuration = YamlConfiguration.loadConfiguration(file); - this.initializer = new ConfigInitializer<>(this); - } - - @Override - public @NotNull YAMLSectionWrapper getConfiguration() { - return YAMLSectionWrapper.of(configuration); - } - - @Override - protected void onReload() throws Exception { - configuration.load(getFile()); - } - - @Override - public @NotNull ConfigurationComments getComments() { - return this.comments; - } - - @Override - public void save() throws Exception { - try { - CommentedYAMLWriter.writeWithComments(this, this.file); - } catch (Exception ex) { - configuration.save(file); - throw ex; - } - } - - @Override - public @NotNull ConfigInitializer getInitializer() { - return this.initializer; - } - - @Override - public String serializeValue(@NotNull String key, @NotNull Object value) { - FileConfiguration temp = new YamlConfiguration(); - temp.set(key, value); - return temp.saveToString(); - } - - @Override - public Set getKeys(@Nullable String sectionKey, boolean deep) { - if (sectionKey == null) return configuration.getKeys(deep); - - ConfigurationSection section = configuration.getConfigurationSection(sectionKey); - if (section == null) return null; - - return section.getKeys(deep); - } - - @Override - public @Nullable Object getValue(@NotNull String key) { - return configuration.get(key); - } - - @Override - public @Nullable List getHeaderComments(@Nullable String key) { - return comments.getHeaderComment(key); - } - -} diff --git a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/YAMLSectionWrapper.java b/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/YAMLSectionWrapper.java deleted file mode 100644 index 2610273..0000000 --- a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/YAMLSectionWrapper.java +++ /dev/null @@ -1,89 +0,0 @@ -package cc.carm.lib.configuration.yaml; - -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; -import org.bspfsystems.yamlconfiguration.configuration.ConfigurationSection; -import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class YAMLSectionWrapper implements ConfigurationWrapper { - - private final ConfigurationSection section; - - private YAMLSectionWrapper(ConfigurationSection section) { - this.section = section; - } - - @Contract("!null->!null") - public static @Nullable YAMLSectionWrapper of(@Nullable ConfigurationSection section) { - return section == null ? null : new YAMLSectionWrapper(section); - } - - @Override - public @NotNull ConfigurationSection getSource() { - return this.section; - } - - @Override - public @NotNull Set getKeys(boolean deep) { - return new LinkedHashSet<>(section.getKeys(deep)); - } - - @Override - public @NotNull Map getValues(boolean deep) { - return section.getValues(deep); - } - - @Override - public void set(@NotNull String path, @Nullable Object value) { - this.section.set(path, value); - } - - @Override - public boolean contains(@NotNull String path) { - return this.section.contains(path); - } - - @Override - public @Nullable Object get(@NotNull String path) { - return this.section.get(path); - } - - @Override - public boolean isList(@NotNull String path) { - return this.section.isList(path); - } - - @Override - public @Nullable List getList(@NotNull String path) { - return this.section.getList(path); - } - - @Override - public boolean isConfigurationSection(@NotNull String path) { - return this.section.isConfigurationSection(path); - } - - @Override - public @Nullable YAMLSectionWrapper getConfigurationSection(@NotNull String path) { - return of(this.section.getConfigurationSection(path)); - } - - @Nullable - public T getSerializable(@NotNull String path, @NotNull Class clazz) { - return getSerializable(path, clazz, null); - } - - @Nullable - @Contract("_, _, !null -> !null") - public T getSerializable(@NotNull String path, @NotNull Class clazz, @Nullable T defaultValue) { - return this.section.getSerializable(path, clazz, defaultValue); - } - -} diff --git a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/YAMLValue.java b/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/YAMLValue.java deleted file mode 100644 index 25179cd..0000000 --- a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/YAMLValue.java +++ /dev/null @@ -1,29 +0,0 @@ -package cc.carm.lib.configuration.yaml; - -import cc.carm.lib.configuration.core.source.ConfigurationProvider; -import cc.carm.lib.configuration.core.value.ValueManifest; -import cc.carm.lib.configuration.core.value.impl.CachedConfigValue; -import cc.carm.lib.configuration.yaml.builder.YAMLConfigBuilder; -import org.jetbrains.annotations.NotNull; - -public abstract class YAMLValue extends CachedConfigValue { - - public static @NotNull YAMLConfigBuilder builder() { - return new YAMLConfigBuilder(); - } - - public YAMLValue(@NotNull ValueManifest manifest) { - super(manifest); - } - - public YAMLConfigProvider getYAMLProvider() { - ConfigurationProvider provider = getProvider(); - if (provider instanceof YAMLConfigProvider) return (YAMLConfigProvider) getProvider(); - else throw new IllegalStateException("Provider is not a YamlConfigProvider"); - } - - public YAMLSectionWrapper getYAMLConfig() { - return getYAMLProvider().getConfiguration(); - } - -} diff --git a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/builder/AbstractYAMLBuilder.java b/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/builder/AbstractYAMLBuilder.java deleted file mode 100644 index 1da838f..0000000 --- a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/builder/AbstractYAMLBuilder.java +++ /dev/null @@ -1,13 +0,0 @@ -package cc.carm.lib.configuration.yaml.builder; - -import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder; -import cc.carm.lib.configuration.yaml.YAMLConfigProvider; - -public abstract class AbstractYAMLBuilder> - extends AbstractConfigBuilder { - - public AbstractYAMLBuilder() { - super(YAMLConfigProvider.class); - } - -} diff --git a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/builder/YAMLConfigBuilder.java b/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/builder/YAMLConfigBuilder.java deleted file mode 100644 index 51ffd9d..0000000 --- a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/builder/YAMLConfigBuilder.java +++ /dev/null @@ -1,14 +0,0 @@ -package cc.carm.lib.configuration.yaml.builder; - -import cc.carm.lib.configuration.core.builder.ConfigBuilder; -import cc.carm.lib.configuration.yaml.builder.serializable.SerializableBuilder; -import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable; -import org.jetbrains.annotations.NotNull; - -public class YAMLConfigBuilder extends ConfigBuilder { - - public @NotNull SerializableBuilder ofSerializable(@NotNull Class valueClass) { - return new SerializableBuilder<>(valueClass); - } - -} diff --git a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/builder/serializable/SerializableBuilder.java b/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/builder/serializable/SerializableBuilder.java deleted file mode 100644 index 955e93a..0000000 --- a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/builder/serializable/SerializableBuilder.java +++ /dev/null @@ -1,29 +0,0 @@ -package cc.carm.lib.configuration.yaml.builder.serializable; - -import cc.carm.lib.configuration.yaml.builder.AbstractYAMLBuilder; -import cc.carm.lib.configuration.yaml.value.ConfiguredSerializable; -import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable; -import org.jetbrains.annotations.NotNull; - -public class SerializableBuilder - extends AbstractYAMLBuilder> { - - protected final @NotNull Class valueClass; - - public SerializableBuilder(@NotNull Class valueClass) { - this.valueClass = valueClass; - } - - @Override - protected @NotNull SerializableBuilder getThis() { - return this; - } - - @Override - public @NotNull ConfiguredSerializable build() { - return new ConfiguredSerializable<>(buildManifest(), this.valueClass); - } - - -} - diff --git a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/value/ConfiguredSerializable.java b/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/value/ConfiguredSerializable.java deleted file mode 100644 index 59f5536..0000000 --- a/impl/yaml/src/main/java/cc/carm/lib/configuration/yaml/value/ConfiguredSerializable.java +++ /dev/null @@ -1,49 +0,0 @@ -package cc.carm.lib.configuration.yaml.value; - -import cc.carm.lib.configuration.core.value.ValueManifest; -import cc.carm.lib.configuration.yaml.YAMLValue; -import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class ConfiguredSerializable extends YAMLValue { - - public static ConfiguredSerializable of(@NotNull Class valueClass) { - return of(valueClass, null); - } - - public static ConfiguredSerializable of(@NotNull Class valueClass, - @Nullable V defaultValue) { - return builder().ofSerializable(valueClass).defaults(defaultValue).build(); - } - - protected final @NotNull Class valueClass; - - public ConfiguredSerializable(@NotNull ValueManifest manifest, @NotNull Class valueClass) { - super(manifest); - this.valueClass = valueClass; - } - - @Override - public @Nullable T get() { - if (!isExpired()) return getCachedOrDefault(); - - try { - // 若未出现错误,则直接更新缓存并返回。 - return updateCache(getYAMLConfig().getSerializable(getConfigPath(), valueClass, getDefaultValue())); - } catch (Exception e) { - // 出现了解析错误,提示并返回默认值。 - e.printStackTrace(); - return getDefaultValue(); - } - - } - - @Override - public void set(@Nullable T value) { - updateCache(value); - setValue(value); - } - - -} diff --git a/impl/yaml/src/test/java/config/DemoConfigTest.java b/impl/yaml/src/test/java/config/DemoConfigTest.java deleted file mode 100644 index b7427cd..0000000 --- a/impl/yaml/src/test/java/config/DemoConfigTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package config; - -import cc.carm.lib.configuration.EasyConfiguration; -import cc.carm.lib.configuration.demo.tests.ConfigurationTest; -import cc.carm.lib.configuration.demo.tests.model.AbstractModel; -import cc.carm.lib.configuration.yaml.YAMLConfigProvider; -import config.model.AnyModel; -import config.model.SomeModel; -import config.source.ModelConfiguration; -import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerialization; -import org.junit.Test; - -import java.io.IOException; - -public class DemoConfigTest { - - static { - ConfigurationSerialization.registerClass(SomeModel.class); - ConfigurationSerialization.registerClass(AnyModel.class); - } - - protected final YAMLConfigProvider provider = EasyConfiguration.from("target/config.yml", "test/test2/config.yml"); - - @Test - public void onTest() { - - ConfigurationTest.testDemo(this.provider); - ConfigurationTest.testInner(this.provider); - - testSerialization(this.provider); - - ConfigurationTest.save(this.provider); - } - - - public static void testSerialization(YAMLConfigProvider provider) { - provider.initialize(ModelConfiguration.class); - System.out.println("----------------------------------------------------"); - - AbstractModel someModel = ModelConfiguration.SOME_MODEL.get(); - if (someModel != null) System.out.println(someModel.getName()); - - AbstractModel anyModel = ModelConfiguration.ANY_MODEL.get(); - if (anyModel != null) System.out.println(anyModel.getName()); - - ModelConfiguration.MODELS.forEach(System.out::println); - ModelConfiguration.MODEL_MAP.forEach((v, anyModel1) -> System.out.println(v + " -> " + anyModel1.toString())); - - - System.out.println("----------------------------------------------------"); - } - - -} diff --git a/impl/yaml/src/test/java/config/model/AnyModel.java b/impl/yaml/src/test/java/config/model/AnyModel.java deleted file mode 100644 index f123d09..0000000 --- a/impl/yaml/src/test/java/config/model/AnyModel.java +++ /dev/null @@ -1,50 +0,0 @@ -package config.model; - -import cc.carm.lib.configuration.demo.tests.model.AbstractModel; -import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable; -import org.bspfsystems.yamlconfiguration.serialization.SerializableAs; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.TestOnly; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -@SerializableAs("AnyModel") -public class AnyModel extends AbstractModel implements ConfigurationSerializable { - - public final boolean bool; - - public AnyModel(@NotNull String name, boolean bool) { - super(name); - this.bool = bool; - } - - @Override - public String toString() { - return "AnyModel{" + - "name='" + name + '\'' + - ", bool=" + bool + - '}'; - } - - @Override - public @NotNull Map serialize() { - Map map = new HashMap<>(); - map.put("name", name); - map.put("state", bool); - return map; - } - - public static AnyModel random() { - return new AnyModel(UUID.randomUUID().toString().substring(0, 5), Math.random() > 0.5); - } - - - @TestOnly - public static AnyModel deserialize(Map args) { - return new AnyModel((String) args.get("name"), (Boolean) args.get("state")); - } - - -} diff --git a/impl/yaml/src/test/java/config/model/SomeModel.java b/impl/yaml/src/test/java/config/model/SomeModel.java deleted file mode 100644 index 65fdf21..0000000 --- a/impl/yaml/src/test/java/config/model/SomeModel.java +++ /dev/null @@ -1,50 +0,0 @@ -package config.model; - -import cc.carm.lib.configuration.demo.tests.model.AbstractModel; -import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable; -import org.bspfsystems.yamlconfiguration.serialization.SerializableAs; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.TestOnly; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -@SerializableAs("SomeModel") -public class SomeModel extends AbstractModel implements ConfigurationSerializable { - - public final int num; - - public SomeModel(@NotNull String name, int num) { - super(name); - this.num = num; - } - - @Override - public String toString() { - return "SomeModel{" + - "name='" + name + '\'' + - ", num=" + num + - '}'; - } - - @Override - public @NotNull Map serialize() { - Map map = new HashMap<>(); - map.put("name", name); - map.put("num", num); - return map; - } - - public static SomeModel random() { - return new SomeModel(UUID.randomUUID().toString().substring(0, 5), (int) (Math.random() * 1000)); - } - - - @TestOnly - public static SomeModel deserialize(Map args) { - return new SomeModel((String) args.get("name"), (Integer) args.get("num")); - } - - -} diff --git a/impl/yaml/src/test/java/config/source/ModelConfiguration.java b/impl/yaml/src/test/java/config/source/ModelConfiguration.java deleted file mode 100644 index 61eab06..0000000 --- a/impl/yaml/src/test/java/config/source/ModelConfiguration.java +++ /dev/null @@ -1,43 +0,0 @@ -package config.source; - -import cc.carm.lib.configuration.core.ConfigurationRoot; -import cc.carm.lib.configuration.core.annotation.ConfigPath; -import cc.carm.lib.configuration.core.annotation.HeaderComment; -import cc.carm.lib.configuration.core.value.ConfigValue; -import cc.carm.lib.configuration.core.value.type.ConfiguredList; -import cc.carm.lib.configuration.core.value.type.ConfiguredMap; -import cc.carm.lib.configuration.core.value.type.ConfiguredSectionMap; -import cc.carm.lib.configuration.demo.tests.model.AbstractModel; -import cc.carm.lib.configuration.yaml.value.ConfiguredSerializable; -import config.model.AnyModel; -import config.model.SomeModel; - -@HeaderComment("以下内容用于测试序列化") -@ConfigPath("model-test") -public class ModelConfiguration extends ConfigurationRoot { - - public static final ConfigValue SOME_MODEL = ConfiguredSerializable.of( - SomeModel.class, SomeModel.random() - ); - - public static final ConfigValue ANY_MODEL = ConfiguredSerializable.of( - AnyModel.class, AnyModel.random() - ); - - public static final ConfiguredList MODELS = ConfiguredList.builderOf(AnyModel.class) - .fromMap() - .parseValue(AnyModel::deserialize).serializeValue(AnyModel::serialize) - .defaults(AnyModel.random(), AnyModel.random(), AnyModel.random()) - .build(); - - public static final ConfiguredSectionMap MODEL_MAP = ConfiguredMap.builderOf(String.class, AnyModel.class) - .asLinkedMap().fromSection() - .parseValue(v -> new AnyModel(v.getString("name", "EMPTY"), v.getBoolean("state", false))) - .serializeValue(AnyModel::serialize) - .defaults(m -> { - m.put("a", AnyModel.random()); - m.put("b", AnyModel.random()); - }) - .build(); - -} diff --git a/pom.xml b/pom.xml index 3e5b228..2df4f9d 100644 --- a/pom.xml +++ b/pom.xml @@ -18,11 +18,16 @@ 3.9.1 core + features/commentable + features/file + features/versioned + + providers/yaml + providers/gson + + + demo - impl/yaml - impl/json - impl/sql - impl/hocon EasyConfiguration diff --git a/impl/json/README.md b/providers/gson/README.md similarity index 100% rename from impl/json/README.md rename to providers/gson/README.md diff --git a/impl/json/pom.xml b/providers/gson/pom.xml similarity index 88% rename from impl/json/pom.xml rename to providers/gson/pom.xml index d2bf56f..7ed8361 100644 --- a/impl/json/pom.xml +++ b/providers/gson/pom.xml @@ -15,7 +15,7 @@ UTF-8 UTF-8 - easyconfiguration-json + easyconfiguration-gson jar @@ -27,6 +27,13 @@ compile + + ${project.parent.groupId} + easyconfiguration-feature-file + ${project.parent.version} + compile + + ${project.parent.groupId} easyconfiguration-demo diff --git a/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONConfigFactory.java b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONConfigFactory.java new file mode 100644 index 0000000..d7f5e39 --- /dev/null +++ b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONConfigFactory.java @@ -0,0 +1,72 @@ +package cc.carm.lib.configuration.source.json; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.file.FileConfigFactory; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class JSONConfigFactory extends FileConfigFactory, JSONConfigFactory> { + + public static JSONConfigFactory from(@NotNull File file) { + return new JSONConfigFactory(file); + } + + public static JSONConfigFactory from(@NotNull File parent, @NotNull String configName) { + return new JSONConfigFactory(new File(parent, configName)); + } + + protected Supplier gsonSupplier = () -> JSONSource.DEFAULT_GSON; + + public JSONConfigFactory(@NotNull File file) { + super(file); + } + + @Override + public JSONConfigFactory self() { + return this; + } + + public JSONConfigFactory gson(@NotNull Supplier gsonSupplier) { + this.gsonSupplier = gsonSupplier; + return self(); + } + + public JSONConfigFactory gson(@NotNull Gson gson) { + return gson(() -> gson); + } + + public JSONConfigFactory gson(@NotNull Consumer builder) { + return gson(() -> { + GsonBuilder gsonBuilder = new GsonBuilder(); + builder.accept(gsonBuilder); + return gsonBuilder.create(); + }); + } + + + @Override + public @NotNull ConfigurationHolder build() { + Gson gson = gsonSupplier.get(); + if (gson == null) throw new NullPointerException("No Gson instance provided."); + + File configFile = this.file; + String sourcePath = this.resourcePath; + + return new ConfigurationHolder(this.adapters, this.options, new ConcurrentHashMap<>(), this.initializer) { + final JSONSource source = new JSONSource(this, 0, configFile, sourcePath, gson); + + @Override + public @NotNull JSONSource config() { + return source; + } + }; + } + + +} diff --git a/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSection.java b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSection.java new file mode 100644 index 0000000..3b93a2c --- /dev/null +++ b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSection.java @@ -0,0 +1,156 @@ +package cc.carm.lib.configuration.source.json; + +import cc.carm.lib.configuration.source.section.ConfigureSection; +import cc.carm.lib.configuration.source.section.ConfigureSource; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class JSONSection implements ConfigureSection { + + protected final @NotNull ConfigureSource source; + protected final @NotNull Map data; + protected final @Nullable JSONSection parent; + + public JSONSection(@NotNull ConfigureSource source, + @NotNull Map data, @Nullable JSONSection parent) { + this.source = source; + this.parent = parent; + this.data = new LinkedHashMap<>(); + + for (Map.Entry entry : data.entrySet()) { + String key = (entry.getKey() == null) ? "null" : entry.getKey().toString(); + + if (entry.getValue() instanceof Map) { + this.data.put(key, new JSONSection(source, (Map) entry.getValue(), this)); + } else if (entry.getValue() instanceof List) { + List list = new ArrayList<>(); + for (Object obj : (List) entry.getValue()) { + if (obj instanceof Map) { + list.add(new JSONSection(source, (Map) obj, this)); + } else { + list.add(obj); + } + } + this.data.put(key, list); + } else { + this.data.put(key, entry.getValue()); + } + } + } + + @Override + public @NotNull ConfigureSource source() { + return this.source; + } + + public @NotNull Map data() { + return this.data; + } + + public @Nullable JSONSection parent() { + return this.parent; + } + + @Override + public @NotNull Map getValues(boolean deep) { + if (deep) { + Map values = new LinkedHashMap<>(); + mapChildrenValues(values, this, null, true); + return Collections.unmodifiableMap(values); + } else return Collections.unmodifiableMap(data()); + } + + @Override + public void set(@NotNull String path, @Nullable Object value) { + if (value instanceof Map) { + value = new JSONSection(source(), (Map) value, this); + } + + JSONSection section = getSectionFor(path); + if (section == this) { + if (value == null) { + this.data.remove(path); + } else { + this.data.put(path, value); + } + } else { + section.set(getChild(path), value); + } + } + + @Override + public boolean contains(@NotNull String path) { + return get(path) != null; + } + + @Override + public @Nullable Object get(@NotNull String path) { + JSONSection section = getSectionFor(path); + return section == this ? data.get(path) : section.get(getChild(path)); + } + + @Override + public boolean isList(@NotNull String path) { + return get(path) instanceof List; + } + + @Override + public @Nullable List getList(@NotNull String path) { + Object val = get(path); + return (val instanceof List) ? (List) val : null; + } + + @Override + public boolean isSection(@NotNull String path) { + return get(path) instanceof JSONSection; + } + + @Override + public @Nullable ConfigureSection getSection(@NotNull String path) { + Object val = get(path); + return (val instanceof ConfigureSection) ? (ConfigureSection) val : null; + } + + private JSONSection getSectionFor(String path) { + int index = path.indexOf(separator()); + if (index == -1) return this; + + String root = path.substring(0, index); + Object section = this.data.get(root); + if (section == null) { + section = new JSONSection(source(), new LinkedHashMap<>(), this); + this.data.put(root, section); + } + + return (JSONSection) section; + } + + private String getChild(String path) { + int index = path.indexOf(separator()); + return (index == -1) ? path : path.substring(index + 1); + } + + + /** + * Map the values of the children of the section to the output map. + * + * @param output The map to map the values to + * @param section The section to map the values from + * @param parent The parent path + * @param deep If the mapping should be deep + * @author md_5, sk89q + */ + protected void mapChildrenValues(@NotNull Map output, @NotNull JSONSection section, + @Nullable String parent, boolean deep) { + for (Map.Entry entry : section.data().entrySet()) { + String path = (parent == null ? "" : parent + separator()) + entry.getKey(); + output.remove(path); + output.put(path, entry.getValue()); + if (deep && entry.getValue() instanceof JSONSection) { + this.mapChildrenValues(output, (JSONSection) entry.getValue(), path, true); + } + } + } +} diff --git a/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSource.java b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSource.java new file mode 100644 index 0000000..3354665 --- /dev/null +++ b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSource.java @@ -0,0 +1,80 @@ +package cc.carm.lib.configuration.source.json; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.file.FileConfigSource; +import cc.carm.lib.configuration.source.section.ConfigureSource; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.*; +import java.util.*; + +public class JSONSource extends FileConfigSource, JSONSource> { + + public static final @NotNull Gson DEFAULT_GSON = new GsonBuilder() + .serializeNulls().disableHtmlEscaping().setPrettyPrinting() + .registerTypeAdapter( + JSONSection.class, + (JsonSerializer) (src, typeOfSrc, context) -> context.serialize(src.data) + ).create(); + + protected final @NotNull Gson gson; + protected @Nullable JSONSection rootSection; + + protected JSONSource(@NotNull ConfigurationHolder holder, long lastUpdateMillis, + @NotNull File file, @Nullable String resourcePath) { + this(holder, lastUpdateMillis, file, resourcePath, DEFAULT_GSON); + } + + protected JSONSource(@NotNull ConfigurationHolder holder, long lastUpdateMillis, + @NotNull File file, @Nullable String resourcePath, @NotNull Gson gson) { + super(holder, lastUpdateMillis, file, resourcePath); + this.gson = gson; + initialize(); + } + + public void initialize() { + try { + initializeFile(); + onReload(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + protected JSONSource self() { + return this; + } + + @Override + public @NotNull ConfigureSource source() { + return this; + } + + @Override + public @NotNull Map original() { + return section().data(); + } + + @Override + public @NotNull JSONSection section() { + return Objects.requireNonNull(this.rootSection, "Section is not initialized"); + } + + @Override + public void save() throws Exception { + fileWriter(writer -> gson.toJson(original(), writer)); + } + + @Override + protected void onReload() throws Exception { + Map data = fileReader(reader -> gson.fromJson(reader, LinkedHashMap.class)); + if (data == null) data = new LinkedHashMap<>(); + this.rootSection = new JSONSection(this, data, null); + this.lastUpdateMillis = System.currentTimeMillis(); // 更新时间 + } +} diff --git a/providers/gson/src/test/java/config/JSONConfigTest.java b/providers/gson/src/test/java/config/JSONConfigTest.java new file mode 100644 index 0000000..07f98aa --- /dev/null +++ b/providers/gson/src/test/java/config/JSONConfigTest.java @@ -0,0 +1,34 @@ +package config; + +import cc.carm.lib.configuration.demo.tests.ConfigurationTest; +import cc.carm.lib.configuration.source.json.JSONConfigFactory; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.value.ConfigValue; +import cc.carm.lib.configuration.value.standard.ConfiguredValue; +import org.junit.Test; + +import java.io.File; + +public class JSONConfigTest { + + protected final ConfigurationHolder holder = JSONConfigFactory + .from(new File("target"), "config.json") + .resourcePath("example.json") + .build(); + + @Test + public void onTest() { + + ConfigValue EXAMPLE = ConfiguredValue.of(false); + EXAMPLE.initialize(this.holder, "example"); + + System.out.println("Example: " + EXAMPLE.get()); + + ConfigurationTest.testDemo(this.holder); + ConfigurationTest.testInner(this.holder); + + ConfigurationTest.save(this.holder); + } + + +} diff --git a/providers/gson/src/test/resources/example.json b/providers/gson/src/test/resources/example.json new file mode 100644 index 0000000..44954eb --- /dev/null +++ b/providers/gson/src/test/resources/example.json @@ -0,0 +1,3 @@ +{ + "example": "true" +} \ No newline at end of file diff --git a/impl/hocon/README.md b/providers/hocon/README.md similarity index 100% rename from impl/hocon/README.md rename to providers/hocon/README.md diff --git a/impl/hocon/pom.xml b/providers/hocon/pom.xml similarity index 100% rename from impl/hocon/pom.xml rename to providers/hocon/pom.xml diff --git a/impl/hocon/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java b/providers/hocon/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java similarity index 100% rename from impl/hocon/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java rename to providers/hocon/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java diff --git a/impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONConfigWrapper.java b/providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONConfigWrapper.java similarity index 98% rename from impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONConfigWrapper.java rename to providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONConfigWrapper.java index 5465e23..600974e 100644 --- a/impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONConfigWrapper.java +++ b/providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONConfigWrapper.java @@ -1,6 +1,5 @@ package cc.carm.lib.configuration.hocon; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; import cc.carm.lib.configuration.hocon.util.HOCONUtils; import com.typesafe.config.*; import org.jetbrains.annotations.NotNull; diff --git a/impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONFileConfigProvider.java b/providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONFileConfigProvider.java similarity index 96% rename from impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONFileConfigProvider.java rename to providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONFileConfigProvider.java index 9c44994..2f12e30 100644 --- a/impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONFileConfigProvider.java +++ b/providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/HOCONFileConfigProvider.java @@ -1,7 +1,6 @@ package cc.carm.lib.configuration.hocon; -import cc.carm.lib.configuration.core.ConfigInitializer; -import cc.carm.lib.configuration.core.source.ConfigurationComments; +import cc.carm.lib.configuration.source.comment.ConfigurationComments; import cc.carm.lib.configuration.core.source.impl.FileConfigProvider; import cc.carm.lib.configuration.hocon.exception.HOCONGetValueException; import cc.carm.lib.configuration.hocon.util.HOCONUtils; diff --git a/impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/exception/HOCONGetValueException.java b/providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/exception/HOCONGetValueException.java similarity index 100% rename from impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/exception/HOCONGetValueException.java rename to providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/exception/HOCONGetValueException.java diff --git a/impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/util/HOCONUtils.java b/providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/util/HOCONUtils.java similarity index 100% rename from impl/hocon/src/main/java/cc/carm/lib/configuration/hocon/util/HOCONUtils.java rename to providers/hocon/src/main/java/cc/carm/lib/configuration/hocon/util/HOCONUtils.java diff --git a/impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/HOCONTest.java b/providers/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/HOCONTest.java similarity index 100% rename from impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/HOCONTest.java rename to providers/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/HOCONTest.java diff --git a/impl/sql/README.md b/providers/sql/README.md similarity index 60% rename from impl/sql/README.md rename to providers/sql/README.md index 2bdf1e3..c2b19fd 100644 --- a/impl/sql/README.md +++ b/providers/sql/README.md @@ -6,17 +6,18 @@ SQL database implementation, support for MySQL or MariaDB. ```mysql CREATE TABLE IF NOT EXISTS conf ( -`namespace` VARCHAR(32) NOT NULL, # 命名空间 (代表其属于谁,类似于单个配置文件地址的概念) -`path` VARCHAR(96) NOT NULL, # 配置路径 (ConfigPath) -`type` TINYINT UNSIGNED NOT NULL DEFAULT 0, # 数据类型 (Integer/Byte/List/Map/...) -`value` MEDIUMTEXT, # 配置项的值 (可能为JSON格式) -`inline_comments` TEXT, # 行内注释 -`header_comments` MEDIUMTEXT, # 顶部注释 -`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, # 创建时间 -`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -PRIMARY KEY (`namespace`, `path`) + `namespace` VARCHAR(32) NOT NULL, # 命名空间 (代表其属于谁,类似于单个配置文件地址的概念) + `path` VARCHAR(96) NOT NULL, # 配置路径 (ConfigPath) + `type` TINYINT UNSIGNED NOT NULL DEFAULT 0, # 数据类型 (Integer/Byte/List/Map/...) + `value` MEDIUMTEXT, # 配置项的值 (可能为JSON格式) + `usage` TEXT, # 配置项的用法,本质是行内注释 + `descriptions` MEDIUMTEXT, # 配置项的描述,本质是顶部注释 + `version` INT UNSIGNED NOT NULL DEFAULT 0, # 配置项的版本 + `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, # 创建时间 + `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`namespace`, `path`) ) ENGINE = InnoDB -DEFAULT CHARSET = utf8mb4; + DEFAULT CHARSET = utf8mb4; ``` ## Dependencies diff --git a/impl/sql/pom.xml b/providers/sql/pom.xml similarity index 100% rename from impl/sql/pom.xml rename to providers/sql/pom.xml diff --git a/impl/sql/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java b/providers/sql/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java similarity index 100% rename from impl/sql/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java rename to providers/sql/src/main/java/cc/carm/lib/configuration/EasyConfiguration.java diff --git a/impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigProvider.java b/providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigProvider.java similarity index 97% rename from impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigProvider.java rename to providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigProvider.java index 453f54a..e2f4ea3 100644 --- a/impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigProvider.java +++ b/providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigProvider.java @@ -1,8 +1,6 @@ package cc.carm.lib.configuration.sql; -import cc.carm.lib.configuration.core.ConfigInitializer; -import cc.carm.lib.configuration.core.source.ConfigurationComments; -import cc.carm.lib.configuration.core.source.ConfigurationProvider; +import cc.carm.lib.configuration.source.comment.ConfigurationComments; import cc.carm.lib.easysql.api.SQLManager; import cc.carm.lib.easysql.api.SQLQuery; import cc.carm.lib.easysql.api.SQLTable; diff --git a/impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigWrapper.java b/providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigWrapper.java similarity index 98% rename from impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigWrapper.java rename to providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigWrapper.java index a817b68..060268d 100644 --- a/impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigWrapper.java +++ b/providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLConfigWrapper.java @@ -1,6 +1,5 @@ package cc.carm.lib.configuration.sql; -import cc.carm.lib.configuration.core.source.ConfigurationWrapper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLValueResolver.java b/providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLValueResolver.java similarity index 100% rename from impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLValueResolver.java rename to providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLValueResolver.java diff --git a/impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLValueTypes.java b/providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLValueTypes.java similarity index 100% rename from impl/sql/src/main/java/cc/carm/lib/configuration/sql/SQLValueTypes.java rename to providers/sql/src/main/java/cc/carm/lib/configuration/sql/SQLValueTypes.java diff --git a/impl/sql/src/test/java/config/SQLConfigTest.java b/providers/sql/src/test/java/config/SQLConfigTest.java similarity index 100% rename from impl/sql/src/test/java/config/SQLConfigTest.java rename to providers/sql/src/test/java/config/SQLConfigTest.java diff --git a/impl/sql/src/test/resources/log4j2.xml b/providers/sql/src/test/resources/log4j2.xml similarity index 100% rename from impl/sql/src/test/resources/log4j2.xml rename to providers/sql/src/test/resources/log4j2.xml diff --git a/impl/yaml/README.md b/providers/yaml/README.md similarity index 100% rename from impl/yaml/README.md rename to providers/yaml/README.md diff --git a/impl/yaml/pom.xml b/providers/yaml/pom.xml similarity index 86% rename from impl/yaml/pom.xml rename to providers/yaml/pom.xml index 93b5db3..88ea416 100644 --- a/impl/yaml/pom.xml +++ b/providers/yaml/pom.xml @@ -37,18 +37,25 @@ ${project.parent.groupId} - easyconfiguration-demo + easyconfiguration-feature-file ${project.parent.version} - test + compile - org.bspfsystems - yamlconfiguration - 3.0.2 + org.yaml + snakeyaml + 2.3 compile + + ${project.parent.groupId} + easyconfiguration-demo + ${project.parent.version} + test + + diff --git a/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLConfigFactory.java b/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLConfigFactory.java new file mode 100644 index 0000000..5458631 --- /dev/null +++ b/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLConfigFactory.java @@ -0,0 +1,4 @@ +package cc.carm.lib.configuration.source.yaml; + +public class YAMLConfigFactory { +} diff --git a/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSection.java b/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSection.java new file mode 100644 index 0000000..d5cbc95 --- /dev/null +++ b/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSection.java @@ -0,0 +1,76 @@ +package cc.carm.lib.configuration.source.yaml; + +import cc.carm.lib.configuration.source.section.ConfigureSection; +import cc.carm.lib.configuration.source.section.ConfigureSource; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.UnmodifiableView; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class YAMLSection implements ConfigureSection { + + protected final @NotNull ConfigureSource source; + protected final @NotNull Map data; + protected final @Nullable YAMLSection parent; + + public YAMLSection(@NotNull ConfigureSource source, + @NotNull Map data, @Nullable YAMLSection parent) { + this.source = source; + this.data = data; + this.parent = parent; + } + + + @Override + public @NotNull ConfigureSource source() { + return this.source; + } + + @Override + public @Nullable YAMLSection parent() { + return this.parent; + } + + @Override + public @NotNull @UnmodifiableView Map getValues(boolean deep) { + return Collections.emptyMap(); + } + + @Override + public void set(@NotNull String path, @Nullable Object value) { + + } + + @Override + public boolean contains(@NotNull String path) { + return false; + } + + @Override + public boolean isList(@NotNull String path) { + return false; + } + + @Override + public @Nullable List getList(@NotNull String path) { + return Collections.emptyList(); + } + + @Override + public boolean isSection(@NotNull String path) { + return false; + } + + @Override + public @Nullable YAMLSection getSection(@NotNull String path) { + return null; + } + + @Override + public @Nullable Object get(@NotNull String path) { + return null; + } +} diff --git a/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSource.java b/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSource.java new file mode 100644 index 0000000..d7af585 --- /dev/null +++ b/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSource.java @@ -0,0 +1,4 @@ +package cc.carm.lib.configuration.source.yaml; + +public class YAMLSource { +} diff --git a/impl/yaml/src/test/java/sample/Sample.java b/providers/yaml/src/test/java/sample/Sample.java similarity index 81% rename from impl/yaml/src/test/java/sample/Sample.java rename to providers/yaml/src/test/java/sample/Sample.java index caaa989..9c458cd 100644 --- a/impl/yaml/src/test/java/sample/Sample.java +++ b/providers/yaml/src/test/java/sample/Sample.java @@ -1,8 +1,5 @@ package sample; -import cc.carm.lib.configuration.EasyConfiguration; -import cc.carm.lib.configuration.core.source.ConfigurationProvider; - public class Sample { public static void main(String[] args) { diff --git a/impl/yaml/src/test/java/sample/SampleConfig.java b/providers/yaml/src/test/java/sample/SampleConfig.java similarity index 70% rename from impl/yaml/src/test/java/sample/SampleConfig.java rename to providers/yaml/src/test/java/sample/SampleConfig.java index 6e32128..d8b53d1 100644 --- a/impl/yaml/src/test/java/sample/SampleConfig.java +++ b/providers/yaml/src/test/java/sample/SampleConfig.java @@ -1,11 +1,11 @@ package sample; -import cc.carm.lib.configuration.core.Configuration; -import cc.carm.lib.configuration.core.annotation.ConfigPath; -import cc.carm.lib.configuration.core.annotation.HeaderComment; -import cc.carm.lib.configuration.core.annotation.InlineComment; -import cc.carm.lib.configuration.core.value.type.ConfiguredList; -import cc.carm.lib.configuration.core.value.type.ConfiguredValue; +import cc.carm.lib.configuration.source.Configuration; +import cc.carm.lib.configuration.annotation.ConfigPath; +import cc.carm.lib.configuration.annotation.HeaderComment; +import cc.carm.lib.configuration.annotation.InlineComment; +import cc.carm.lib.configuration.value.standard.ConfiguredList; +import cc.carm.lib.configuration.value.standard.ConfiguredValue; import java.util.UUID; diff --git a/impl/yaml/src/test/resources/test/test2/config.yml b/providers/yaml/src/test/resources/test/test2/config.yml similarity index 100% rename from impl/yaml/src/test/resources/test/test2/config.yml rename to providers/yaml/src/test/resources/test/test2/config.yml