1
mirror of https://github.com/CarmJos/EasyConfiguration.git synced 2026-06-04 18:48:20 +08:00

feat!(core): Finished the design of core module

This commit is contained in:
2025-02-09 03:49:37 +08:00
parent 205db6e5b9
commit 45ca8b02d4
35 changed files with 758 additions and 613 deletions
@@ -1,6 +1,6 @@
package cc.carm.lib.configuration.adapter;
import cc.carm.lib.configuration.function.ConfigDataFunction;
import cc.carm.lib.configuration.function.DataFunction;
import cc.carm.lib.configuration.source.ConfigurationProvider;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -15,14 +15,14 @@ public class ValueAdapterRegistry {
protected final Set<ValueAdapter<?>> adapters = new HashSet<>();
public <FROM, TO> void register(@NotNull Class<FROM> from, @NotNull Class<TO> to,
@Nullable ConfigDataFunction<FROM, TO> parser,
@Nullable ConfigDataFunction<TO, FROM> serializer) {
@Nullable DataFunction<FROM, TO> parser,
@Nullable DataFunction<TO, FROM> serializer) {
register(ValueType.of(from), ValueType.of(to), parser, serializer);
}
public <FROM, TO> void register(@NotNull ValueType<FROM> from, @NotNull ValueType<TO> to,
@Nullable ConfigDataFunction<FROM, TO> parser,
@Nullable ConfigDataFunction<TO, FROM> serializer) {
@Nullable DataFunction<FROM, TO> parser,
@Nullable DataFunction<TO, FROM> serializer) {
ValueAdapter<FROM> fromAdapter = adapterOf(from);
if (fromAdapter == null) throw new IllegalArgumentException("No adapter for type " + from);
register(to,
@@ -3,17 +3,27 @@ package cc.carm.lib.configuration.adapter.strandard;
import cc.carm.lib.configuration.adapter.ValueAdapter;
import cc.carm.lib.configuration.adapter.ValueParser;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.function.ConfigDataFunction;
import cc.carm.lib.configuration.function.DataFunction;
import org.jetbrains.annotations.NotNull;
public class PrimitiveAdapters<T> extends ValueAdapter<T> {
import java.util.Arrays;
public static final PrimitiveAdapters<?>[] ADAPTERS = new PrimitiveAdapters[]{
public class PrimitiveAdapter<T> extends ValueAdapter<T> {
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<Enum<?>> ofEnum() {
ValueAdapter<Enum<?>> adapter = new ValueAdapter<>(new ValueType<Enum<?>>() {
@@ -23,86 +33,92 @@ public class PrimitiveAdapters<T> extends ValueAdapter<T> {
return adapter;
}
public static PrimitiveAdapters<String> ofString() {
public static PrimitiveAdapter<String> ofString() {
return of(String.class, o -> o instanceof String ? (String) o : o.toString());
}
public static PrimitiveAdapters<Boolean> ofBoolean() {
return of(Boolean.class, o -> o instanceof Boolean ? (Boolean) o : Boolean.parseBoolean(o.toString()));
public static PrimitiveAdapter<Boolean> 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 PrimitiveAdapters<Boolean> ofBooleanType() {
public static PrimitiveAdapter<Boolean> ofBooleanType() {
return of(boolean.class, o -> o instanceof Boolean ? (Boolean) o : Boolean.parseBoolean(o.toString()));
}
public static PrimitiveAdapters<Character> ofCharacter() {
public static PrimitiveAdapter<Character> ofCharacter() {
return of(Character.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0));
}
public static PrimitiveAdapters<Character> ofCharacterType() {
public static PrimitiveAdapter<Character> ofCharacterType() {
return of(char.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0));
}
public static PrimitiveAdapters<Integer> ofInteger() {
public static PrimitiveAdapter<Integer> ofInteger() {
return ofNumber(Integer.class, Number::intValue, Integer::parseInt);
}
public static PrimitiveAdapters<Integer> ofIntegerType() {
public static PrimitiveAdapter<Integer> ofIntegerType() {
return ofNumber(int.class, Number::intValue, Integer::parseInt);
}
public static PrimitiveAdapters<Long> ofLong() {
public static PrimitiveAdapter<Long> ofLong() {
return ofNumber(Long.class, Number::longValue, Long::parseLong);
}
public static PrimitiveAdapters<Long> ofLongType() {
public static PrimitiveAdapter<Long> ofLongType() {
return ofNumber(long.class, Number::longValue, Long::parseLong);
}
public static PrimitiveAdapters<Double> ofDouble() {
public static PrimitiveAdapter<Double> ofDouble() {
return ofNumber(Double.class, Number::doubleValue, Double::parseDouble);
}
public static PrimitiveAdapters<Double> ofDoubleType() {
public static PrimitiveAdapter<Double> ofDoubleType() {
return ofNumber(double.class, Number::doubleValue, Double::parseDouble);
}
public static PrimitiveAdapters<Float> ofFloat() {
public static PrimitiveAdapter<Float> ofFloat() {
return ofNumber(Float.class, Number::floatValue, Float::parseFloat);
}
public static PrimitiveAdapters<Float> ofFloatType() {
public static PrimitiveAdapter<Float> ofFloatType() {
return ofNumber(float.class, Number::floatValue, Float::parseFloat);
}
public static PrimitiveAdapters<Short> ofShort() {
public static PrimitiveAdapter<Short> ofShort() {
return ofNumber(Short.class, Number::shortValue, Short::parseShort);
}
public static PrimitiveAdapters<Short> ofShortType() {
public static PrimitiveAdapter<Short> ofShortType() {
return ofNumber(short.class, Number::shortValue, Short::parseShort);
}
public static PrimitiveAdapters<Byte> ofByte() {
public static PrimitiveAdapter<Byte> ofByte() {
return ofNumber(Byte.class, Number::byteValue, Byte::parseByte);
}
public static PrimitiveAdapters<Byte> ofByteType() {
public static PrimitiveAdapter<Byte> ofByteType() {
return ofNumber(byte.class, Number::byteValue, Byte::parseByte);
}
public static <T> PrimitiveAdapters<T> of(@NotNull Class<T> clazz,
@NotNull ConfigDataFunction<Object, T> function) {
return new PrimitiveAdapters<>(clazz, (p, type, data) -> function.handle(data));
public static <T> PrimitiveAdapter<T> of(@NotNull Class<T> clazz,
@NotNull DataFunction<Object, T> function) {
return new PrimitiveAdapter<>(clazz, (p, type, data) -> function.handle(data));
}
public static <T extends Number> PrimitiveAdapters<T> ofNumber(@NotNull Class<T> numberClass,
@NotNull ConfigDataFunction<Number, T> castFunction,
@NotNull ConfigDataFunction<String, T> parseFunction) {
public static <T extends Number> PrimitiveAdapter<T> ofNumber(@NotNull Class<T> numberClass,
@NotNull DataFunction<Number, T> castFunction,
@NotNull DataFunction<String, T> parseFunction) {
return of(numberClass, o -> o instanceof Number ? castFunction.handle((Number) o) : parseFunction.handle(o.toString()));
}
protected PrimitiveAdapters(@NotNull Class<T> valueType, @NotNull ValueParser<T> deserializer) {
protected PrimitiveAdapter(@NotNull Class<T> valueType, @NotNull ValueParser<T> deserializer) {
super(ValueType.of(valueType), (provider, type, value) -> value, deserializer);
}
@@ -1,7 +1,5 @@
package cc.carm.lib.configuration.annotation;
import cc.carm.lib.configuration.source.loader.PathGenerator;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -2,11 +2,15 @@ package cc.carm.lib.configuration.builder;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.source.ConfigurationProvider;
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<
@@ -21,6 +25,8 @@ public abstract class AbstractConfigBuilder<
protected @Nullable String path;
protected @NotNull Supplier<TYPE> defaultValueSupplier = () -> null;
protected @NotNull BiConsumer<ConfigurationProvider<?>, String> initializer = (provider, path) -> {
};
protected AbstractConfigBuilder(Class<? super PROVIDER> providerClass, ValueType<TYPE> type) {
this.providerClass = providerClass;
@@ -35,7 +41,7 @@ public abstract class AbstractConfigBuilder<
public abstract @NotNull RESULT build();
public @NotNull SELF from(@Nullable PROVIDER provider) {
public @NotNull SELF provider(@Nullable PROVIDER provider) {
this.provider = provider;
return self();
}
@@ -45,6 +51,19 @@ public abstract class AbstractConfigBuilder<
return self();
}
public @NotNull SELF initializer(@NotNull BiConsumer<ConfigurationProvider<?>, String> initializer) {
this.initializer = initializer;
return self();
}
public @NotNull SELF append(@NotNull BiConsumer<ConfigurationProvider<?>, String> initializer) {
return initializer(initializer.andThen(initializer));
}
public @NotNull SELF append(@NotNull Consumer<ConfigurationProvider<?>> initializer) {
return append((provider, path) -> initializer.accept(provider));
}
public @NotNull SELF defaults(@Nullable TYPE defaultValue) {
return defaults(() -> defaultValue);
}
@@ -54,8 +73,19 @@ public abstract class AbstractConfigBuilder<
return self();
}
public <M> @NotNull SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) {
return append((provider, path) -> metaConsumer.accept(provider.metadata(path)));
}
public <M> @NotNull SELF meta(@NotNull ConfigurationMetadata<M> type, @Nullable M value) {
return meta(holder -> holder.set(type, value));
}
protected @NotNull ValueManifest<TYPE> buildManifest() {
return new ValueManifest<>(type(), this.provider, this.path, this.defaultValueSupplier);
return new ValueManifest<>(
type(), this.defaultValueSupplier, this.initializer,
this.provider, this.path
);
}
}
@@ -3,8 +3,8 @@ 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.ConfigDataFunction;
import cc.carm.lib.configuration.function.ConfigValueHandler;
import cc.carm.lib.configuration.function.DataFunction;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigurationSection;
import cc.carm.lib.configuration.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
@@ -21,32 +21,32 @@ public abstract class AbstractSectionBuilder<
protected final @NotNull ValueType<PARAM> paramType;
protected @NotNull ConfigValueHandler<ConfigurationSection, PARAM> parser;
protected @NotNull ConfigValueHandler<PARAM, ? extends Map<Object, Object>> serializer;
protected @NotNull ValueHandler<ConfigurationSection, PARAM> parser;
protected @NotNull ValueHandler<PARAM, ? extends Map<Object, Object>> serializer;
public AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<PARAM> paramType,
@NotNull ConfigValueHandler<ConfigurationSection, PARAM> parser,
@NotNull ConfigValueHandler<PARAM, ? extends Map<Object, Object>> serializer) {
@NotNull ValueHandler<ConfigurationSection, PARAM> parser,
@NotNull ValueHandler<PARAM, ? extends Map<Object, Object>> serializer) {
super(type);
this.paramType = paramType;
this.parser = parser;
this.serializer = serializer;
}
public @NotNull SELF parse(ConfigDataFunction<ConfigurationSection, PARAM> valueParser) {
public @NotNull SELF parse(DataFunction<ConfigurationSection, PARAM> valueParser) {
return parse((p, section) -> valueParser.handle(section));
}
public @NotNull SELF parse(ConfigValueHandler<ConfigurationSection, PARAM> valueParser) {
public @NotNull SELF parse(ValueHandler<ConfigurationSection, PARAM> valueParser) {
this.parser = valueParser;
return self();
}
public @NotNull SELF serialize(ConfigDataFunction<PARAM, ? extends Map<Object, Object>> serializer) {
public @NotNull SELF serialize(DataFunction<PARAM, ? extends Map<Object, Object>> serializer) {
return serialize((p, value) -> serializer.handle(value));
}
public @NotNull SELF serialize(ConfigValueHandler<PARAM, ? extends Map<Object, Object>> serializer) {
public @NotNull SELF serialize(ValueHandler<PARAM, ? extends Map<Object, Object>> serializer) {
this.serializer = serializer;
return self();
}
@@ -3,8 +3,8 @@ 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.ConfigDataFunction;
import cc.carm.lib.configuration.function.ConfigValueHandler;
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;
@@ -15,13 +15,13 @@ public abstract class AbstractSourceBuilder<
protected final @NotNull ValueType<SOURCE> sourceType;
protected final @NotNull ValueType<PARAM> paramType;
protected @NotNull ConfigValueHandler<SOURCE, PARAM> valueParser;
protected @NotNull ConfigValueHandler<PARAM, SOURCE> valueSerializer;
protected @NotNull ValueHandler<SOURCE, PARAM> valueParser;
protected @NotNull ValueHandler<PARAM, SOURCE> valueSerializer;
public AbstractSourceBuilder(@NotNull ValueType<V> type,
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<PARAM> paramType,
@NotNull ConfigValueHandler<SOURCE, PARAM> parser,
@NotNull ConfigValueHandler<PARAM, SOURCE> serializer) {
@NotNull ValueHandler<SOURCE, PARAM> parser,
@NotNull ValueHandler<PARAM, SOURCE> serializer) {
super(type);
this.sourceType = sourceType;
this.paramType = paramType;
@@ -29,21 +29,21 @@ public abstract class AbstractSourceBuilder<
this.valueSerializer = serializer;
}
public @NotNull SELF parse(ConfigDataFunction<SOURCE, PARAM> parser) {
public @NotNull SELF parse(DataFunction<SOURCE, PARAM> parser) {
return parse((p, source) -> parser.handle(source));
}
public @NotNull SELF parse(@NotNull ConfigValueHandler<SOURCE, PARAM> parser) {
public @NotNull SELF parse(@NotNull ValueHandler<SOURCE, PARAM> parser) {
this.valueParser = parser;
return self();
}
public @NotNull SELF serialize(@NotNull ConfigValueHandler<PARAM, SOURCE> serializer) {
public @NotNull SELF serialize(@NotNull ValueHandler<PARAM, SOURCE> serializer) {
this.valueSerializer = serializer;
return self();
}
public @NotNull SELF serialize(@NotNull ConfigDataFunction<PARAM, SOURCE> serializer) {
public @NotNull SELF serialize(@NotNull DataFunction<PARAM, SOURCE> serializer) {
return serialize((p, value) -> serializer.handle(value));
}
@@ -1,7 +1,7 @@
package cc.carm.lib.configuration.builder.list;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.function.ConfigValueHandler;
import cc.carm.lib.configuration.function.ValueHandler;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -19,7 +19,7 @@ public class ConfigListBuilder<V> {
}
public <S> @NotNull SourceListBuilder<S, V> from(@NotNull ValueType<S> sourceType) {
return new SourceListBuilder<>(sourceType, type, ConfigValueHandler.required(), ConfigValueHandler.required(), ArrayList::new);
return new SourceListBuilder<>(sourceType, type, ValueHandler.required(), ValueHandler.required(), ArrayList::new);
}
public @NotNull SourceListBuilder<String, V> fromString() {
@@ -27,7 +27,7 @@ public class ConfigListBuilder<V> {
}
public @NotNull SectionListBuilder<V> fromSection() {
return new SectionListBuilder<>(type, ConfigValueHandler.required(), ConfigValueHandler.required(), ArrayList::new);
return new SectionListBuilder<>(type, ValueHandler.required(), ValueHandler.required(), ArrayList::new);
}
@@ -2,7 +2,7 @@ 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.ConfigValueHandler;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigurationSection;
import cc.carm.lib.configuration.value.standard.ConfiguredList;
import org.jetbrains.annotations.NotNull;
@@ -15,8 +15,8 @@ public class SectionListBuilder<V> extends AbstractSectionBuilder<List<V>, V, Co
protected @NotNull Supplier<? extends List<V>> constructor;
public SectionListBuilder(@NotNull ValueType<V> paramType,
@NotNull ConfigValueHandler<ConfigurationSection, V> parser,
@NotNull ConfigValueHandler<V, ? extends Map<Object, Object>> serializer,
@NotNull ValueHandler<ConfigurationSection, V> parser,
@NotNull ValueHandler<V, ? extends Map<Object, Object>> serializer,
@NotNull Supplier<? extends List<V>> constructor) {
super(new ValueType<List<V>>() {
}, paramType, parser, serializer);
@@ -2,7 +2,7 @@ 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.ConfigValueHandler;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.value.standard.ConfiguredList;
import org.jetbrains.annotations.NotNull;
@@ -18,7 +18,7 @@ public class SourceListBuilder<SOURCE, V>
protected @NotNull Supplier<? extends List<V>> constructor;
public SourceListBuilder(@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<V> paramType,
@NotNull ConfigValueHandler<SOURCE, V> parser, @NotNull ConfigValueHandler<V, SOURCE> serializer,
@NotNull ValueHandler<SOURCE, V> parser, @NotNull ValueHandler<V, SOURCE> serializer,
@NotNull Supplier<? extends List<V>> constructor) {
super(new ValueType<List<V>>() {
}, sourceType, paramType, parser, serializer);
@@ -1,7 +1,7 @@
package cc.carm.lib.configuration.builder.value;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.function.ConfigValueHandler;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigurationSection;
import org.jetbrains.annotations.NotNull;
@@ -20,12 +20,12 @@ public class ConfigValueBuilder<V> {
}
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull ValueType<S> sourceType) {
return from(sourceType, ConfigValueHandler.required(), ConfigValueHandler.required());
return from(sourceType, ValueHandler.required(), ValueHandler.required());
}
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull ValueType<S> sourceType,
@NotNull ConfigValueHandler<S, V> valueParser,
@NotNull ConfigValueHandler<V, S> valueSerializer) {
@NotNull ValueHandler<S, V> valueParser,
@NotNull ValueHandler<V, S> valueSerializer) {
return new SourceValueBuilder<>(sourceType, this.type, valueParser, valueSerializer);
}
@@ -34,12 +34,12 @@ public class ConfigValueBuilder<V> {
}
public @NotNull SectionValueBuilder<V> fromSection() {
return fromSection(ConfigValueHandler.required(), ConfigValueHandler.required());
return fromSection(ValueHandler.required(), ValueHandler.required());
}
public @NotNull SectionValueBuilder<V> fromSection(
@NotNull ConfigValueHandler<ConfigurationSection, V> valueParser,
@NotNull ConfigValueHandler<V, ? extends Map<Object, Object>> valueSerializer
@NotNull ValueHandler<ConfigurationSection, V> valueParser,
@NotNull ValueHandler<V, ? extends Map<Object, Object>> valueSerializer
) {
return new SectionValueBuilder<>(this.type, valueParser, valueSerializer);
}
@@ -2,7 +2,7 @@ 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.ConfigValueHandler;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigurationSection;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import org.jetbrains.annotations.NotNull;
@@ -12,8 +12,8 @@ import java.util.Map;
public class SectionValueBuilder<V> extends AbstractSectionBuilder<V, V, ConfiguredValue<V>, SectionValueBuilder<V>> {
public SectionValueBuilder(@NotNull ValueType<V> type,
@NotNull ConfigValueHandler<ConfigurationSection, V> parser,
@NotNull ConfigValueHandler<V, ? extends Map<Object, Object>> serializer) {
@NotNull ValueHandler<ConfigurationSection, V> parser,
@NotNull ValueHandler<V, ? extends Map<Object, Object>> serializer) {
super(type, type, parser, serializer);
}
@@ -2,7 +2,7 @@ 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.ConfigValueHandler;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import org.jetbrains.annotations.NotNull;
@@ -10,7 +10,7 @@ public class SourceValueBuilder<S, V> extends AbstractSourceBuilder<V, S, V, Con
public SourceValueBuilder(@NotNull ValueType<S> sourceType, @NotNull ValueType<V> valueType,
@NotNull ConfigValueHandler<S, V> parser, @NotNull ConfigValueHandler<V, S> serializer) {
@NotNull ValueHandler<S, V> parser, @NotNull ValueHandler<V, S> serializer) {
super(valueType, sourceType, valueType, parser, serializer);
}
@@ -7,39 +7,39 @@ import org.jetbrains.annotations.NotNull;
import java.util.Objects;
@FunctionalInterface
public interface ConfigDataFunction<T, R> {
public interface DataFunction<T, R> {
@NotNull R handle(@NotNull T data) throws Exception;
default <V> @NotNull ConfigDataFunction<T, V> andThen(@NotNull ConfigDataFunction<? super R, V> after) {
default <V> @NotNull DataFunction<T, V> andThen(@NotNull DataFunction<? super R, V> after) {
Objects.requireNonNull(after);
return data -> after.handle(handle(data));
}
@Contract(pure = true)
static <T> @NotNull ConfigDataFunction<T, T> identity() {
static <T> @NotNull DataFunction<T, T> identity() {
return input -> input;
}
@Contract(pure = true)
static <T> @NotNull ConfigDataFunction<T, T> identity(Class<T> type) {
static <T> @NotNull DataFunction<T, T> identity(Class<T> type) {
return input -> input;
}
@Contract(pure = true)
static <T, V> @NotNull ConfigDataFunction<T, V> required() {
static <T, V> @NotNull DataFunction<T, V> required() {
return input -> {
throw new IllegalArgumentException("Please specify the value parser.");
};
}
@Contract(pure = true)
static <T> @NotNull ConfigDataFunction<T, Object> toObject() {
static <T> @NotNull DataFunction<T, Object> toObject() {
return input -> input;
}
@Contract(pure = true)
static <V> @NotNull ConfigDataFunction<Object, V> castObject(Class<V> valueClass) {
static <V> @NotNull DataFunction<Object, V> castObject(Class<V> 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<T, R> {
}
@Contract(pure = true)
static <V> @NotNull ConfigDataFunction<String, V> castFromString(Class<V> valueClass) {
static <V> @NotNull DataFunction<String, V> castFromString(Class<V> 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<T, R> {
}
@Contract(pure = true)
static <T> @NotNull ConfigDataFunction<T, String> castToString() {
static <T> @NotNull DataFunction<T, String> 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<T, R> {
}
@Contract(pure = true)
static <V> @NotNull ConfigDataFunction<String, V> parseString(Class<V> valueClass) {
static <V> @NotNull DataFunction<String, V> parseString(Class<V> 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<T, R> {
}
@Contract(pure = true)
static @NotNull ConfigDataFunction<Object, Integer> intValue() {
static @NotNull DataFunction<Object, Integer> intValue() {
return input -> {
if (input instanceof Integer) {
return (Integer) input;
@@ -83,7 +83,7 @@ public interface ConfigDataFunction<T, R> {
}
@Contract(pure = true)
static @NotNull ConfigDataFunction<Object, Short> shortValue() {
static @NotNull DataFunction<Object, Short> shortValue() {
return input -> {
if (input instanceof Short) {
return (Short) input;
@@ -94,7 +94,7 @@ public interface ConfigDataFunction<T, R> {
}
@Contract(pure = true)
static @NotNull ConfigDataFunction<Object, Double> doubleValue() {
static @NotNull DataFunction<Object, Double> doubleValue() {
return input -> {
if (input instanceof Double) {
return (Double) input;
@@ -105,7 +105,7 @@ public interface ConfigDataFunction<T, R> {
}
@Contract(pure = true)
static @NotNull ConfigDataFunction<Object, Byte> byteValue() {
static @NotNull DataFunction<Object, Byte> byteValue() {
return input -> {
if (input instanceof Byte) {
return (Byte) input;
@@ -116,7 +116,7 @@ public interface ConfigDataFunction<T, R> {
}
@Contract(pure = true)
static @NotNull ConfigDataFunction<Object, Float> floatValue() {
static @NotNull DataFunction<Object, Float> floatValue() {
return input -> {
if (input instanceof Float) {
return (Float) input;
@@ -127,7 +127,7 @@ public interface ConfigDataFunction<T, R> {
}
@Contract(pure = true)
static @NotNull ConfigDataFunction<Object, Long> longValue() {
static @NotNull DataFunction<Object, Long> longValue() {
return input -> {
if (input instanceof Long) {
return (Long) input;
@@ -138,7 +138,7 @@ public interface ConfigDataFunction<T, R> {
}
@Contract(pure = true)
static @NotNull ConfigDataFunction<Object, Boolean> booleanValue() {
static @NotNull DataFunction<Object, Boolean> booleanValue() {
return input -> {
if (input instanceof Boolean) {
return (Boolean) input;
@@ -10,11 +10,11 @@ import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@FunctionalInterface
public interface ConfigValueHandler<T, R> {
public interface ValueHandler<T, R> {
@Nullable R handle(@NotNull ConfigurationProvider<?> provider, @NotNull T data) throws Exception;
default <V> ConfigValueHandler<T, V> andThen(@NotNull ConfigValueHandler<R, V> after) {
default <V> ValueHandler<T, V> andThen(@NotNull ValueHandler<R, V> after) {
Objects.requireNonNull(after);
return ((provider, data) -> {
R result = handle(provider, data);
@@ -23,7 +23,7 @@ public interface ConfigValueHandler<T, R> {
});
}
default <V> ConfigValueHandler<V, R> compose(@NotNull ConfigValueHandler<? super V, ? extends T> before) {
default <V> ValueHandler<V, R> compose(@NotNull ValueHandler<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return ((provider, data) -> {
T result = before.handle(provider, data);
@@ -32,7 +32,7 @@ public interface ConfigValueHandler<T, R> {
});
}
default <V> ConfigValueHandler<V, R> compose(@NotNull ConfigDataFunction<? super V, ? extends T> before) {
default <V> ValueHandler<V, R> compose(@NotNull DataFunction<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return ((provider, data) -> {
T result = before.handle(data);
@@ -41,23 +41,23 @@ public interface ConfigValueHandler<T, R> {
}
@Contract(pure = true)
static <T> @NotNull ConfigValueHandler<T, T> identity() {
static <T> @NotNull ValueHandler<T, T> identity() {
return (provider, input) -> input;
}
@Contract(pure = true)
static <T> @NotNull ConfigValueHandler<T, Object> toObject() {
static <T> @NotNull ValueHandler<T, Object> toObject() {
return ConfigurationProvider::serialize;
}
@Contract(pure = true)
static <T> @NotNull ConfigValueHandler<Object, T> fromObject(ValueType<T> type) {
static <T> @NotNull ValueHandler<Object, T> fromObject(ValueType<T> type) {
return (provider, input) -> provider.deserialize(type, input);
}
@Contract(pure = true)
static <T, V> @NotNull ConfigValueHandler<T, V> required() {
static <T, V> @NotNull ValueHandler<T, V> required() {
return (provider, input) -> {
throw new IllegalArgumentException("Please specify the value parser.");
};
@@ -1,58 +0,0 @@
package cc.carm.lib.configuration.meta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
import java.util.function.Supplier;
public class PathMetadata<T> {
public static <T> PathMetadata<T> of() {
return of(() -> null);
}
public static <T> PathMetadata<T> of(T defaults) {
return of(() -> defaults);
}
public static <T> PathMetadata<T> of(@NotNull Supplier<@Nullable T> defaults) {
return of(v -> defaults.get());
}
public static <T> PathMetadata<T> of(@NotNull Function<String, @Nullable T> defaults) {
return new PathMetadata<>(defaults);
}
protected Function<String, @Nullable T> defaultFunction;
public PathMetadata(@NotNull Function<String, @Nullable T> defaults) {
this.defaultFunction = defaults;
}
public boolean isDefault(String path, @NotNull T value) {
return value.equals(defaults(path));
}
public boolean hasDefaults(String path) {
return defaults(path) != null;
}
public T getDefault(String path, @Nullable T suppliedValue) {
T defaults = defaults(path);
return defaults == null ? suppliedValue : defaults;
}
public @Nullable T defaults(String path) {
return defaultFunction.apply(path);
}
public void setDefaults(Function<String, T> defaultFunction) {
this.defaultFunction = defaultFunction;
}
public void setDefaults(T value) {
setDefaults((v) -> value);
}
}
@@ -1,7 +1,7 @@
package cc.carm.lib.configuration.source;
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
import cc.carm.lib.configuration.source.loader.ConfigurationLoader;
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
import cc.carm.lib.configuration.source.loader.PathGenerator;
import cc.carm.lib.configuration.source.option.ConfigurationOption;
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
@@ -13,8 +13,8 @@ import java.util.function.Function;
public abstract class ConfigurationFactory<SOURCE extends ConfigurationSource<SOURCE, ?>, PROVIDER extends ConfigurationProvider<SOURCE>, SELF> {
protected Function<PROVIDER, ConfigurationLoader> loaderFunction = PROVIDER -> new ConfigurationLoader();
protected Consumer<ConfigurationLoader> loaderConsumer = loader -> {
protected Function<PROVIDER, ConfigurationInitializer> loaderFunction = PROVIDER -> new ConfigurationInitializer();
protected Consumer<ConfigurationInitializer> loaderConsumer = loader -> {
};
protected ValueAdapterRegistry adapters = new ValueAdapterRegistry();
@@ -22,23 +22,23 @@ public abstract class ConfigurationFactory<SOURCE extends ConfigurationSource<SO
public abstract SELF self();
public SELF loader(Function<PROVIDER, ConfigurationLoader> loaderFunction) {
public SELF loader(Function<PROVIDER, ConfigurationInitializer> loaderFunction) {
this.loaderFunction = loaderFunction;
return self();
}
public SELF loader(ConfigurationLoader loader) {
public SELF loader(ConfigurationInitializer loader) {
return loader(PROVIDER -> loader);
}
public SELF loader(Consumer<ConfigurationLoader> loaderConsumer) {
public SELF loader(Consumer<ConfigurationInitializer> loaderConsumer) {
this.loaderConsumer = this.loaderConsumer.andThen(loaderConsumer);
return self();
}
public SELF pathGenerator(PathGenerator pathGenerator) {
public SELF pathGenerator(PathGenerator generator) {
return loader(loader -> {
loader.setPathGenerator(pathGenerator);
loader.pathGenerator(generator);
});
}
@@ -3,9 +3,8 @@ 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.meta.PathMetadata;
import cc.carm.lib.configuration.source.loader.ConfigurationLoader;
import cc.carm.lib.configuration.source.option.ConfigurationOption;
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.ConfigurationSource;
import cc.carm.lib.configuration.value.ValueManifest;
@@ -18,20 +17,22 @@ import java.util.Map;
public class ConfigurationProvider<S extends ConfigurationSource<S, ?>> {
protected final @NotNull S source;
protected final @NotNull ConfigurationLoader loader;
protected final @NotNull ValueAdapterRegistry adapters;
protected final @NotNull ConfigurationOptionHolder options;
protected final @NotNull Map<String, Map<PathMetadata<?>, Object>> pathMetadata;
protected final @NotNull Map<String, ConfigurationMetaHolder> metadata;
public ConfigurationProvider(@NotNull S source, @NotNull ConfigurationLoader loader,
protected final @NotNull ConfigurationInitializer initializer;
public ConfigurationProvider(@NotNull S source,
@NotNull ValueAdapterRegistry adapters,
@NotNull ConfigurationOptionHolder options,
@NotNull Map<String, Map<PathMetadata<?>, Object>> pathMetadata) {
@NotNull Map<String, ConfigurationMetaHolder> metadata,
@NotNull ConfigurationInitializer initializer) {
this.source = source;
this.loader = loader;
this.initializer = initializer;
this.adapters = adapters;
this.options = options;
this.pathMetadata = pathMetadata;
this.metadata = metadata;
}
public @NotNull S source() {
@@ -50,112 +51,21 @@ public class ConfigurationProvider<S extends ConfigurationSource<S, ?>> {
return options;
}
public <T> @NotNull T option(@NotNull ConfigurationOption<T> option) {
return options.get(option);
public @NotNull Map<String, ConfigurationMetaHolder> metadata() {
return this.metadata;
}
public <T> void option(@NotNull ConfigurationOption<T> option, @NotNull T value) {
options.set(option, value);
public @NotNull ConfigurationMetaHolder metadata(@Nullable String path) {
return metadata().computeIfAbsent(path, k -> new ConfigurationMetaHolder());
}
public ValueAdapterRegistry adapters() {
return this.adapters;
}
public ConfigurationLoader loader() {
return loader;
}
public @NotNull Map<String, Map<PathMetadata<?>, Object>> pathMetadata() {
return pathMetadata;
}
public @NotNull Map<PathMetadata<?>, Object> metadata(@NotNull String path) {
return pathMetadata().computeIfAbsent(path, k -> new java.util.HashMap<>());
}
/**
* Get the value of option.
*
* @param type {@link PathMetadata}
* @param defaultValue Default value if the value of option is not set.
* @param <V> Value type
* @return Value of option
*/
@SuppressWarnings("unchecked")
@Contract("_,_, !null -> !null")
public <V> @Nullable V meta(@NotNull String path,
@NotNull PathMetadata<V> type, @Nullable V defaultValue) {
return (V) metadata(path).getOrDefault(type, type.getDefault(path, defaultValue));
}
/**
* Get the value of option.
*
* @param type {@link PathMetadata}
* @param <V> Value type
* @return Value of option
*/
public <V> @Nullable V meta(@NotNull String path, @NotNull PathMetadata<V> type) {
return meta(path, type, null);
}
public boolean hasMeta(@NotNull String path, @NotNull PathMetadata<?> type) {
return metadata(path).containsKey(type) || type.hasDefaults(path);
}
/**
* Set the value of meta, if the value is null, the meta will be removed.
* <br> Will only be changed in current holder.
*
* @param type {@link PathMetadata}
* @param value Value of meta
* @param <V> Value type
* @return Previous value of meta
*/
@SuppressWarnings("unchecked")
public <V> @Nullable V setMeta(@NotNull String path, @NotNull PathMetadata<V> type, @Nullable V value) {
if (value == null || type.isDefault(path, value)) {
return (V) metadata(path).remove(type);
} else {
return (V) metadata(path).put(type, value);
}
}
/**
* Set the value of meta, if the value is null, the meta will not be changed.
* <br> Will only be changed in current holder.
*
* @param type {@link PathMetadata}
* @param value Value of meta
* @param <V> Value type
*/
public <V> void setMetaIfAbsent(@NotNull String path, @NotNull PathMetadata<V> type, @Nullable V value) {
if (value == null || type.isDefault(path, value)) {
metadata(path).remove(type);
} else {
metadata(path).putIfAbsent(type, value);
}
}
/**
* Set the value of meta, if the value is null, the meta will not be changed.
* <br> Will only be changed in current holder.
*
* @param type {@link PathMetadata}
* @param value Value of meta
* @param <V> Value type
*/
@SuppressWarnings("unchecked")
public <V> @Nullable V setMetaIfPresent(@NotNull String path, @NotNull PathMetadata<V> type, @Nullable V value) {
Object exists = metadata(path).get(type);
if (exists == null) return null;
if (value == null || type.isDefault(path, value)) {
return (V) metadata(path).remove(type);
} else {
return (V) metadata(path).put(type, value);
}
public ConfigurationInitializer initializer() {
return initializer;
}
@Contract("_,null -> null")
@@ -167,7 +77,7 @@ public class ConfigurationProvider<S extends ConfigurationSource<S, ?>> {
public <T> T deserialize(@NotNull ValueType<T> type, @Nullable Object source) throws Exception {
return adapters().deserialize(this, type, source);
}
@Contract("null -> null")
public <T> Object serialize(@Nullable T value) throws Exception {
return adapters().serialize(this, value);
@@ -175,7 +85,7 @@ public class ConfigurationProvider<S extends ConfigurationSource<S, ?>> {
public void load(Class<? extends Configuration> configClass) {
try {
loader.load(this, configClass);
initializer.initialize(this, configClass);
} catch (Exception e) {
e.printStackTrace();
}
@@ -183,7 +93,7 @@ public class ConfigurationProvider<S extends ConfigurationSource<S, ?>> {
public void load(@NotNull Configuration config) {
try {
loader.load(this, config);
initializer.initialize(this, config);
} catch (Exception e) {
e.printStackTrace();
}
@@ -0,0 +1,32 @@
package cc.carm.lib.configuration.source.loader;
import cc.carm.lib.configuration.source.ConfigurationProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@FunctionalInterface
public interface ConfigInitializeHandler<T> {
static <T> ConfigInitializeHandler<T> start() {
return (provider, path, value) -> {
};
}
void whenInitialize(@NotNull ConfigurationProvider<?> provider, @Nullable String path, @NotNull T value) throws Exception;
default ConfigInitializeHandler<T> andThen(ConfigInitializeHandler<T> after) {
return (provider, path, value) -> {
whenInitialize(provider, path, value);
after.whenInitialize(provider, path, value);
};
}
default ConfigInitializeHandler<T> compose(ConfigInitializeHandler<T> before) {
return (provider, path, value) -> {
before.whenInitialize(provider, path, value);
whenInitialize(provider, path, value);
};
}
}
@@ -0,0 +1,177 @@
package cc.carm.lib.configuration.source.loader;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.source.ConfigurationProvider;
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<Field> fieldInitializer;
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer;
public ConfigurationInitializer() {
this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start());
}
public ConfigurationInitializer(@NotNull PathGenerator pathGenerator,
@NotNull ConfigInitializeHandler<Field> fieldInitializer,
@NotNull ConfigInitializeHandler<Class<? extends Configuration>> 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<Field> fieldInitializer() {
return fieldInitializer;
}
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
this.fieldInitializer = fieldInitializer;
}
public ConfigInitializeHandler<Class<? extends Configuration>> classInitializer() {
return classInitializer;
}
public void classInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
this.classInitializer = classInitializer;
}
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
this.fieldInitializer = this.fieldInitializer.andThen(fieldInitializer);
}
public void appendClassInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
this.classInitializer = this.classInitializer.andThen(classInitializer);
}
public <T, A extends Annotation> void registerAnnotation(@NotNull Class<A> annotation,
@NotNull ConfigurationMetadata<T> metadata,
@NotNull Function<A, T> extractor) {
appendFieldInitializer((provider, path, field) -> {
A data = field.getAnnotation(annotation);
if (data == null) return;
provider.metadata(path).setIfAbsent(metadata, extractor.apply(data));
});
appendClassInitializer((provider, path, clazz) -> {
A data = clazz.getAnnotation(annotation);
if (data == null) return;
provider.metadata(path).setIfAbsent(metadata, extractor.apply(data));
});
}
public @Nullable String getFieldPath(ConfigurationProvider<?> provider, @Nullable String parentPath, @NotNull Field field) {
return pathGenerator.getFieldPath(provider, parentPath, field);
}
public @Nullable String getClassPath(ConfigurationProvider<?> provider, @Nullable String parentPath,
@NotNull Class<?> clazz, @Nullable Field clazzField) {
return pathGenerator.getClassPath(provider, parentPath, clazz, clazzField);
}
public void initialize(ConfigurationProvider<?> provider, @NotNull Configuration config) throws Exception {
initializeInstance(provider, config, null, null);
if (provider.options().get(StandardOptions.SET_DEFAULTS)) provider.save();
}
public void initialize(ConfigurationProvider<?> provider, @NotNull Class<? extends Configuration> clazz) throws Exception {
initializeStaticClass(provider, clazz, null, null);
if (provider.options().get(StandardOptions.SET_DEFAULTS)) provider.save();
}
// 针对实例类的初始化方法
protected void initializeInstance(@NotNull ConfigurationProvider<?> provider,
@NotNull Configuration root, @Nullable String parentPath, @Nullable Field configField) {
String path = getClassPath(provider, parentPath, root.getClass(), configField);
try {
this.classInitializer.whenInitialize(provider, path, root.getClass());
} catch (Exception e) {
e.printStackTrace();
}
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(provider, root, field, path));
}
// 针对静态类的初始化方法
@SuppressWarnings("unchecked")
protected void initializeStaticClass(@NotNull ConfigurationProvider<?> provider,
@NotNull Class<?> clazz, @Nullable String parentPath, @Nullable Field configField) {
if (!Configuration.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类
String path = getClassPath(provider, parentPath, clazz, configField);
try {
this.classInitializer.whenInitialize(provider, path, (Class<? extends Configuration>) clazz);
} catch (Exception e) {
e.printStackTrace();
}
for (Field field : clazz.getDeclaredFields()) {
initializeField(provider, clazz, field, path);
}
if (provider.options().get(StandardOptions.LOAD_SUB_CLASSES)) {
Class<?>[] classes = clazz.getDeclaredClasses();
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
initializeStaticClass(provider, classes[i], path, null);
}
}
}
protected void initializeField(@NotNull ConfigurationProvider<?> provider,
@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(provider, parent, field);
if (path == null) return;
value.initialize(provider, path);
try {
this.fieldInitializer.whenInitialize(provider, path, field);
} catch (Exception e) {
e.printStackTrace();
}
} else if (source instanceof Configuration && object instanceof Configuration) {
// 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。
initializeInstance(provider, (Configuration) object, parent, field);
} else if (source instanceof Class<?> && object instanceof Class<?>) {
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
initializeStaticClass(provider, (Class<?>) object, parent, field);
}
// 以上判断实现以下规范:
// - 实例类中仅加载 ConfigValue实例 与 Configuration实例
// - 静态类中仅加载 静态ConfigValue实例 与 静态Configuration类
} catch (IllegalAccessException ignored) {
}
}
}
@@ -1,133 +0,0 @@
package cc.carm.lib.configuration.source.loader;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.source.ConfigurationProvider;
import cc.carm.lib.configuration.source.option.ConfigurationOptions;
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.io.File;
import java.lang.reflect.Field;
import java.util.Arrays;
/**
* Configuration loader,
* used to load configuration values from {@link Configuration} classes.
*/
public class ConfigurationLoader {
public static final Field PATH_FIELD;
public static final Field PROVIDER_FIELD;
static {
try {
PATH_FIELD = ValueManifest.class.getDeclaredField("path");
PATH_FIELD.setAccessible(true);
PROVIDER_FIELD = ValueManifest.class.getDeclaredField("provider");
PROVIDER_FIELD.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
protected PathGenerator pathGenerator;
public ConfigurationLoader() {
this(StandardPathGenerator.of());
}
public ConfigurationLoader(PathGenerator pathGenerator) {
this.pathGenerator = pathGenerator;
}
public void setPathGenerator(PathGenerator pathGenerator) {
this.pathGenerator = pathGenerator;
}
public PathGenerator getPathGenerator() {
return pathGenerator;
}
public @Nullable String getFieldPath(ConfigurationProvider<?> provider, @Nullable String parentPath, @NotNull Field field) {
return pathGenerator.getFieldPath(provider, parentPath, field);
}
public @Nullable String getClassPath(ConfigurationProvider<?> provider, @Nullable String parentPath,
@NotNull Class<?> clazz, @Nullable Field clazzField) {
return pathGenerator.getClassPath(provider, parentPath, clazz, clazzField);
}
public void load(ConfigurationProvider<?> provider, @NotNull Configuration config) throws Exception {
initializeInstance(provider, config, null, null);
if (provider.option(ConfigurationOptions.SET_DEFAULTS)) provider.save();
}
public void load(ConfigurationProvider<?> provider, @NotNull Class<? extends Configuration> clazz) throws Exception {
initializeStaticClass(provider, clazz, null, null);
if (provider.option(ConfigurationOptions.SET_DEFAULTS)) provider.save();
}
// 针对实例类的初始化方法
private void initializeInstance(@NotNull ConfigurationProvider<?> provider,
@NotNull Configuration root, @Nullable String parentPath, @Nullable Field configField) {
String path = getClassPath(provider, parentPath, root.getClass(), configField);
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(provider, root, field, path));
}
// 针对静态类的初始化方法
private void initializeStaticClass(@NotNull ConfigurationProvider<?> provider,
@NotNull Class<?> clazz, @Nullable String parentPath, @Nullable Field configField) {
if (!Configuration.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类
String path = getClassPath(provider, parentPath, clazz, configField);
for (Field field : clazz.getDeclaredFields()) {
initializeField(provider, clazz, field, path);
}
if (!provider.option(ConfigurationOptions.LOAD_SUB_CLASSES)) return;
Class<?>[] classes = clazz.getDeclaredClasses();
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
initializeStaticClass(provider, classes[i], path, null);
}
}
private void initializeField(@NotNull ConfigurationProvider<?> provider,
@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(provider, parent, field);
if (path == null) return;
insertIfAbsent(value, PATH_FIELD, path);
insertIfAbsent(value, PROVIDER_FIELD, provider);
} else if (source instanceof Configuration && object instanceof Configuration) {
// 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。
initializeInstance(provider, (Configuration) object, parent, field);
} else if (source instanceof Class<?> && object instanceof Class<?>) {
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
initializeStaticClass(provider, (Class<?>) object, parent, field);
}
// 以上判断实现以下规范:
// - 实例类中仅加载 ConfigValue实例 与 Configuration实例
// - 静态类中仅加载 静态ConfigValue实例 与 静态Configuration类
} catch (IllegalAccessException ignored) {
}
}
private void insertIfAbsent(@NotNull ValueManifest<?> value, @NotNull Field field, @NotNull Object obj) {
try {
if (field.get(obj) == null) field.set(obj, value);
} catch (IllegalAccessException ignored) {
}
}
}
@@ -1,27 +1,96 @@
package cc.carm.lib.configuration.source.loader;
import cc.carm.lib.configuration.annotation.ConfigPath;
import cc.carm.lib.configuration.source.ConfigurationProvider;
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 interface PathGenerator {
public class PathGenerator {
@Nullable String getFieldPath(@NotNull ConfigurationProvider<?> provider,
@Nullable String parentPath, @NotNull Field field);
public static PathGenerator of() {
return of(PathGenerator::covertPathName);
}
@Nullable String getClassPath(@NotNull ConfigurationProvider<?> provider,
@Nullable String parentPath, @NotNull Class<?> clazz, @Nullable Field clazzField);
public static PathGenerator of(UnaryOperator<String> pathConverter) {
return new PathGenerator(pathConverter);
}
protected UnaryOperator<String> pathConverter;
public PathGenerator(UnaryOperator<String> pathConverter) {
this.pathConverter = pathConverter;
}
public @NotNull UnaryOperator<String> getPathConverter() {
return pathConverter;
}
public void setPathConverter(UnaryOperator<String> pathConverter) {
this.pathConverter = pathConverter;
}
public String covertPath(String name) {
return pathConverter.apply(name);
}
public @Nullable String getFieldPath(@NotNull ConfigurationProvider<?> provider,
@Nullable String parentPath, @NotNull Field field) {
ConfigPath path = field.getAnnotation(ConfigPath.class);
if (path == null) return link(provider, parentPath, false, field.getName()); // No annotation, use field name.
else return link(provider, parentPath, path.root(), select(path.value(), field.getName()));
}
public @Nullable String getClassPath(@NotNull ConfigurationProvider<?> provider,
@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(provider, parentPath, clazzPath.root(), clazzPath.value());
if (clazzField == null) {
return link(provider, parentPath, false, clazz.getSimpleName()); // No field, use class name.
}
ConfigPath fieldPath = clazzField.getAnnotation(ConfigPath.class);
if (fieldPath == null) return link(provider, parentPath, false, clazzField.getName());
else return getFieldPath(provider, 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 ConfigurationProvider<?> provider,
@Nullable String parent, boolean root, @Nullable String path) {
if (path == null || path.isEmpty()) return root ? null : parent;
return root || parent == null ? covertPath(path) : parent + pathSeparator(provider) + covertPath(path);
}
public static boolean isBlank(String path) {
return path == null || path.replace(" ", "").isEmpty();
}
public static char pathSeparator(ConfigurationProvider<?> provider) {
return provider.options().get(StandardOptions.PATH_SEPARATOR);
}
/**
* Get the configuration name of the specified element.
* Use the naming convention of all lowercase and "-" links.
* <p>
* e.g. "SOME_NAME" -> "some-name"
*
* @param name source name
* @return the final path
*/
static String covertPathName(String name) {
public static String covertPathName(String name) {
return name
// Replace all uppercase letters with dashes
.replaceAll("[A-Z]", "-$0")
@@ -40,4 +109,5 @@ public interface PathGenerator {
.toLowerCase();
}
}
@@ -1,86 +0,0 @@
package cc.carm.lib.configuration.source.loader;
import cc.carm.lib.configuration.annotation.ConfigPath;
import cc.carm.lib.configuration.source.ConfigurationProvider;
import cc.carm.lib.configuration.source.option.ConfigurationOptions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.function.UnaryOperator;
public class StandardPathGenerator implements PathGenerator {
public static StandardPathGenerator of() {
return of(PathGenerator::covertPathName);
}
public static StandardPathGenerator of(UnaryOperator<String> pathConverter) {
return new StandardPathGenerator(pathConverter);
}
protected UnaryOperator<String> pathConverter;
public StandardPathGenerator(UnaryOperator<String> pathConverter) {
this.pathConverter = pathConverter;
}
public @NotNull UnaryOperator<String> getPathConverter() {
return pathConverter;
}
public void setPathConverter(UnaryOperator<String> pathConverter) {
this.pathConverter = pathConverter;
}
public String covertPath(String name) {
return pathConverter.apply(name);
}
public char pathSeparator(ConfigurationProvider<?> provider) {
return provider.option(ConfigurationOptions.PATH_SEPARATOR);
}
@Override
public @Nullable String getFieldPath(@NotNull ConfigurationProvider<?> provider,
@Nullable String parentPath, @NotNull Field field) {
ConfigPath path = field.getAnnotation(ConfigPath.class);
if (path == null) return link(provider, parentPath, false, field.getName()); // No annotation, use field name.
else return link(provider, parentPath, path.root(), select(path.value(), field.getName()));
}
@Override
public @Nullable String getClassPath(@NotNull ConfigurationProvider<?> provider,
@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(provider, parentPath, clazzPath.root(), clazzPath.value());
if (clazzField == null) {
return link(provider, parentPath, false, clazz.getSimpleName()); // No field, use class name.
}
ConfigPath fieldPath = clazzField.getAnnotation(ConfigPath.class);
if (fieldPath == null) return link(provider, parentPath, false, clazzField.getName());
else return getFieldPath(provider, parentPath, clazzField);
}
protected String select(String path, String defaultValue) {
if (path == null || path.isEmpty()) return defaultValue;
else return isBlank(path) ? null : path;
}
protected boolean isBlank(String path) {
return path == null || path.replace(" ", "").isEmpty();
}
protected @Nullable String link(@NotNull ConfigurationProvider<?> provider, @Nullable String parent, boolean root, @Nullable String path) {
if (path == null || path.isEmpty()) return root ? null : parent;
return root || parent == null ? covertPath(path) : parent + pathSeparator(provider) + covertPath(path);
}
}
@@ -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<ConfigurationMetadata<?>, Object> values;
public ConfigurationMetaHolder() {
this(new ConcurrentHashMap<>());
}
public ConfigurationMetaHolder(@NotNull Map<ConfigurationMetadata<?>, Object> values) {
this.values = values;
}
public @NotNull Map<ConfigurationMetadata<?>, 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 <V> Value type
* @return Value of option
*/
@SuppressWarnings("unchecked")
@Contract("_,!null -> !null")
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> 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 <V> Value type
* @return Value of option
*/
@SuppressWarnings("unchecked")
@Contract("_,!null -> !null")
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type, Supplier<@Nullable V> defaultValue) {
return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue));
}
/**
* Get the value of option.
*
* @param type {@link ConfigurationMetadata}
* @param <V> Value type
* @return Value of option
*/
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> 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.
* <br> Will only be changed in current holder.
*
* @param type {@link ConfigurationMetadata}
* @param value Value of meta
* @param <V> Value type
* @return Previous value of meta
*/
@SuppressWarnings("unchecked")
public <V> @Nullable V set(@NotNull ConfigurationMetadata<V> 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.
* <br> Will only be changed in current holder.
*
* @param type {@link ConfigurationMetadata}
* @param value Value of meta
* @param <V> Value type
*/
public <V> void setIfAbsent(@NotNull ConfigurationMetadata<V> 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.
* <br> Will only be changed in current holder.
*
* @param type {@link ConfigurationMetadata}
* @param value Value of meta
* @param <V> Value type
*/
@SuppressWarnings("unchecked")
public <V> @Nullable V setIfPresent(@NotNull ConfigurationMetadata<V> 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);
}
}
}
@@ -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<T> {
public static <T> ConfigurationMetadata<T> of() {
return of(() -> null);
}
public static <T> ConfigurationMetadata<T> of(T defaults) {
return of(() -> defaults);
}
public static <T> ConfigurationMetadata<T> 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<T> defaultFunction) {
this.defaultsSupplier = defaultFunction;
}
public void setDefaults(T value) {
setDefaults(() -> value);
}
}
@@ -9,7 +9,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class ConfigurationOptionHolder {
public static <V> @NotNull ConfigurationOptionHolder of(@NotNull Map<ConfigurationOption<V>, V> options) {
public static @NotNull ConfigurationOptionHolder of(@NotNull Map<ConfigurationOption<?>, Object> options) {
return new ConfigurationOptionHolder(new ConcurrentHashMap<>(options));
}
@@ -23,7 +23,7 @@ public class ConfigurationOptionHolder {
this.options = options;
}
public @NotNull Map<ConfigurationOption<?>, Object> options() {
public @NotNull Map<ConfigurationOption<?>, Object> values() {
return options;
}
@@ -36,7 +36,7 @@ public class ConfigurationOptionHolder {
*/
@SuppressWarnings("unchecked")
public <V> @NotNull V get(@NotNull ConfigurationOption<V> type) {
return Optional.ofNullable(options().get(type)).map(v -> (V) v).orElseGet(type::defaults);
return Optional.ofNullable(values().get(type)).map(v -> (V) v).orElseGet(type::defaults);
}
/**
@@ -50,9 +50,9 @@ public class ConfigurationOptionHolder {
@SuppressWarnings("unchecked")
public <V> @Nullable V set(@NotNull ConfigurationOption<V> type, @Nullable V value) {
if (value == null) {
return (V) options().remove(type);
return (V) values().remove(type);
} else {
return (V) options().put(type, value);
return (V) values().put(type, value);
}
}
@@ -4,7 +4,7 @@ import cc.carm.lib.configuration.Configuration;
import static cc.carm.lib.configuration.source.option.ConfigurationOption.of;
public interface ConfigurationOptions {
public interface StandardOptions {
/**
* The configuration path separator.
@@ -1,6 +1,6 @@
package cc.carm.lib.configuration.source.section;
import cc.carm.lib.configuration.function.ConfigDataFunction;
import cc.carm.lib.configuration.function.DataFunction;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,18 +41,18 @@ public interface ConfigurationSection {
return get(path, null, clazz);
}
default @Nullable <T> T get(@NotNull String path, @NotNull ConfigDataFunction<Object, T> parser) {
default @Nullable <T> T get(@NotNull String path, @NotNull DataFunction<Object, T> parser) {
return get(path, null, parser);
}
@Contract("_,!null,_->!null")
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class<T> clazz) {
return get(path, defaultValue, ConfigDataFunction.castObject(clazz));
return get(path, defaultValue, DataFunction.castObject(clazz));
}
@Contract("_,!null,_->!null")
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
@NotNull ConfigDataFunction<Object, T> parser) {
@NotNull DataFunction<Object, T> parser) {
Object value = get(path);
if (value != null) {
try {
@@ -75,7 +75,7 @@ public interface ConfigurationSection {
@Contract("_, !null -> !null")
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
return get(path, def, ConfigDataFunction.booleanValue());
return get(path, def, DataFunction.booleanValue());
}
default @Nullable Boolean isByte(@NotNull String path) {
@@ -88,7 +88,7 @@ public interface ConfigurationSection {
@Contract("_, !null -> !null")
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
return get(path, def, ConfigDataFunction.byteValue());
return get(path, def, DataFunction.byteValue());
}
default boolean isShort(@NotNull String path) {
@@ -101,7 +101,7 @@ public interface ConfigurationSection {
@Contract("_, !null -> !null")
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
return get(path, def, ConfigDataFunction.shortValue());
return get(path, def, DataFunction.shortValue());
}
@@ -115,7 +115,7 @@ public interface ConfigurationSection {
@Contract("_, !null -> !null")
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
return get(path, def, ConfigDataFunction.intValue());
return get(path, def, DataFunction.intValue());
}
@@ -129,7 +129,7 @@ public interface ConfigurationSection {
@Contract("_, !null -> !null")
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
return get(path, def, ConfigDataFunction.longValue());
return get(path, def, DataFunction.longValue());
}
@@ -143,7 +143,7 @@ public interface ConfigurationSection {
@Contract("_, !null -> !null")
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
return get(path, def, ConfigDataFunction.floatValue());
return get(path, def, DataFunction.floatValue());
}
@@ -157,7 +157,7 @@ public interface ConfigurationSection {
@Contract("_, !null -> !null")
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
return get(path, def, ConfigDataFunction.doubleValue());
return get(path, def, DataFunction.doubleValue());
}
@@ -188,47 +188,47 @@ public interface ConfigurationSection {
return get(path, def, String.class);
}
default <V> @NotNull List<V> getList(@NotNull String path, @NotNull ConfigDataFunction<Object, V> parser) {
default <V> @NotNull List<V> getList(@NotNull String path, @NotNull DataFunction<Object, V> parser) {
return parseList(getList(path), parser);
}
@Unmodifiable
default @NotNull List<String> getStringList(@NotNull String path) {
return getList(path, ConfigDataFunction.castToString());
return getList(path, DataFunction.castToString());
}
@Unmodifiable
default @NotNull List<Integer> getIntegerList(@NotNull String path) {
return getList(path, ConfigDataFunction.intValue());
return getList(path, DataFunction.intValue());
}
@Unmodifiable
default @NotNull List<Long> getLongList(@NotNull String path) {
return getList(path, ConfigDataFunction.longValue());
return getList(path, DataFunction.longValue());
}
@Unmodifiable
default @NotNull List<Double> getDoubleList(@NotNull String path) {
return getList(path, ConfigDataFunction.doubleValue());
return getList(path, DataFunction.doubleValue());
}
@Unmodifiable
default @NotNull List<Float> getFloatList(@NotNull String path) {
return getList(path, ConfigDataFunction.floatValue());
return getList(path, DataFunction.floatValue());
}
@Unmodifiable
default @NotNull List<Byte> getByteList(@NotNull String path) {
return getList(path, ConfigDataFunction.byteValue());
return getList(path, DataFunction.byteValue());
}
@Unmodifiable
default @NotNull List<Character> getCharList(@NotNull String path) {
return getList(path, ConfigDataFunction.castObject(Character.class));
return getList(path, DataFunction.castObject(Character.class));
}
@Unmodifiable
static <T> @NotNull List<T> parseList(@Nullable List<?> list, ConfigDataFunction<Object, T> parser) {
static <T> @NotNull List<T> parseList(@Nullable List<?> list, DataFunction<Object, T> parser) {
if (list == null) return Collections.emptyList();
List<T> values = new ArrayList<>();
for (Object o : list) {
@@ -10,7 +10,7 @@ import java.util.Optional;
public abstract class ConfigValue<T> extends ValueManifest<T> {
protected ConfigValue(@NotNull ValueManifest<T> manifest) {
super(manifest.type, manifest.provider, manifest.path, manifest.defaultSupplier);
super(manifest);
}
/**
@@ -37,7 +37,7 @@ public abstract class ConfigValue<T> extends ValueManifest<T> {
* @throws NullPointerException 对应数据为空时抛出
*/
public @NotNull T getNotNull() {
return Objects.requireNonNull(getOrDefault(), "Value(" + path() + ") [" + type() + "] is null.");
return Objects.requireNonNull(getOrDefault(), "Value(" + type() + ") @[" + path() + "] is null.");
}
public @NotNull Optional<@Nullable T> optional() {
@@ -80,4 +80,16 @@ public abstract class ConfigValue<T> extends ValueManifest<T> {
return Objects.equals(defaults(), get());
}
/**
* Try to save the configuration.
* <br>To save multiple modifications,
* it is recommended to call {@link ConfigurationProvider#save()}
* after all modifications are completed instead of this.
*
* @throws Exception
*/
public void save() throws Exception {
provider().save();
}
}
@@ -2,41 +2,65 @@ package cc.carm.lib.configuration.value;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.source.ConfigurationProvider;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
import cc.carm.lib.configuration.source.section.ConfigurationSource;
import cc.carm.lib.configuration.meta.PathMetadata;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
public class ValueManifest<T> {
protected final @NotNull ValueType<T> type;
protected final @NotNull BiConsumer<@NotNull ConfigurationProvider<?>, @NotNull String> initializer;
protected @Nullable ConfigurationProvider<?> provider;
protected @Nullable String path; // Section path
protected @NotNull Supplier<@Nullable T> defaultSupplier;
public ValueManifest(ValueType<T> type) {
this(type, null, null, () -> null);
public ValueManifest(@NotNull ValueType<T> type) {
this(type, () -> null, EMPTY_INITIALIZER, null, null);
}
public ValueManifest(@NotNull T defaultValue) {
this(ValueType.of(defaultValue), null, null, () -> defaultValue);
this(ValueType.of(defaultValue), () -> defaultValue);
}
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier) {
this(type, null, null, defaultSupplier);
this(type, defaultSupplier, EMPTY_INITIALIZER, null, null);
}
public ValueManifest(@NotNull ValueType<T> type,
@Nullable ConfigurationProvider<?> provider, @Nullable String path,
@NotNull Supplier<@Nullable T> defaultSupplier) {
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier,
@NotNull BiConsumer<@NotNull ConfigurationProvider<?>, @NotNull String> initializer) {
this(type, defaultSupplier, initializer, null, null);
}
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier,
@NotNull BiConsumer<@NotNull ConfigurationProvider<?>, @NotNull String> initializer,
@Nullable ConfigurationProvider<?> provider, @Nullable String path) {
this.type = type;
this.initializer = initializer;
this.defaultSupplier = defaultSupplier;
this.provider = provider;
this.path = path;
this.defaultSupplier = defaultSupplier;
initialize();
}
protected ValueManifest(@NotNull ValueManifest<T> manifest) {
this(manifest.type, manifest.defaultSupplier, manifest.initializer, manifest.provider, manifest.path);
}
public void initialize(@NotNull ConfigurationProvider<?> provider, @NotNull String path) {
this.provider = provider;
this.path = path;
initialize();
}
protected void initialize() {
if (provider != null && path != null) this.initializer.accept(provider, path);
}
public @NotNull ValueType<T> type() {
@@ -81,6 +105,10 @@ public class ValueManifest<T> {
return provider().source();
}
public ConfigurationMetaHolder metadata() {
return provider().metadata(path());
}
protected Object getData() {
return config().get(path());
}
@@ -89,8 +117,8 @@ public class ValueManifest<T> {
config().set(path(), value);
}
public Map<PathMetadata<?>, Object> metadata() {
return provider().metadata(path());
}
private static final @NotNull BiConsumer<@NotNull ConfigurationProvider<?>, @NotNull String> EMPTY_INITIALIZER = (provider, path) -> {
};
}
+6 -9
View File
@@ -1,17 +1,14 @@
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapters;
import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter;
import cc.carm.lib.configuration.source.ConfigurationProvider;
import cc.carm.lib.configuration.source.loader.ConfigurationLoader;
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import cc.carm.test.config.TestSource;
import org.junit.Test;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class AdaptTest {
@@ -20,8 +17,8 @@ public class AdaptTest {
public void test() throws Exception {
ValueAdapterRegistry registry = new ValueAdapterRegistry();
registry.register(PrimitiveAdapters.ADAPTERS);
registry.register(PrimitiveAdapters.ofEnum());
registry.register(PrimitiveAdapter.ADAPTERS);
registry.register(PrimitiveAdapter.ofEnum());
registry.register(ValueType.of(Long.class), ValueType.of(Duration.class), Duration::ofMillis, Duration::toMillis);
@@ -32,8 +29,8 @@ public class AdaptTest {
);
ConfigurationProvider<TestSource> provider = new ConfigurationProvider<>(
new TestSource(), new ConfigurationLoader(),
registry, new ConfigurationOptionHolder(), new ConcurrentHashMap<>()
new TestSource(), registry, new ConfigurationOptionHolder(),
new ConcurrentHashMap<>(), new ConfigurationInitializer()
);
LocalTime v = registry.deserialize(provider, LocalTime.class, 600000L);
-1
View File
@@ -1,4 +1,3 @@
import cc.carm.lib.configuration.source.loader.PathGenerator;
import org.junit.Test;
public class NameTest {
@@ -4,7 +4,7 @@ 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.ConfigurationProvider;
import cc.carm.lib.configuration.source.loader.ConfigurationLoader;
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
import org.junit.Test;
@@ -14,10 +14,13 @@ public class LoaderTest {
@Test
public void test() throws Exception {
ConfigurationProvider<TestSource> provider = new ConfigurationProvider<>(new TestSource(), new ConfigurationLoader(), new ValueAdapterRegistry(), new ConfigurationOptionHolder(), new ConcurrentHashMap<>());
ConfigurationProvider<TestSource> provider = new ConfigurationProvider<>(
new TestSource(), new ValueAdapterRegistry(), new ConfigurationOptionHolder(),
new ConcurrentHashMap<>(), new ConfigurationInitializer()
);
ConfigurationLoader loader = new ConfigurationLoader();
loader.load(provider, ROOT.class);
ConfigurationInitializer loader = new ConfigurationInitializer();
loader.initialize(provider, ROOT.class);
}
interface ROOT extends Configuration {