mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 18:48:20 +08:00
feat(json): Implement json sources
This commit is contained in:
@@ -55,13 +55,13 @@ public class ValueAdapter<TYPE>
|
||||
@Override
|
||||
public Object serialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull TYPE value) throws Exception {
|
||||
if (serializer == null) throw new UnsupportedOperationException("Serializer is not supported");
|
||||
return serializer.serialize(provider, type, value);
|
||||
return serializer.serialize(holder, type, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TYPE parse(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull Object value) throws Exception {
|
||||
if (deserializer == null) throw new UnsupportedOperationException("Deserializer is not supported");
|
||||
return deserializer.parse(provider, type, value);
|
||||
return deserializer.parse(holder, type, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,7 +101,7 @@ public class ValueAdapterRegistry {
|
||||
|
||||
@Contract("_,_,null -> null")
|
||||
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||
return deserialize(provider, ValueType.of(type), source);
|
||||
return deserialize(holder, ValueType.of(type), source);
|
||||
}
|
||||
|
||||
@Contract("_,_,null -> null")
|
||||
@@ -110,7 +110,7 @@ public class ValueAdapterRegistry {
|
||||
if (type.isInstance(source)) return type.cast(source); // Not required to deserialize
|
||||
ValueAdapter<T> adapter = adapterOf(type);
|
||||
if (adapter == null) throw new RuntimeException("No adapter for type " + type);
|
||||
return adapter.parse(provider, type, source);
|
||||
return adapter.parse(holder, type, source);
|
||||
}
|
||||
|
||||
@Contract("_,null -> null")
|
||||
@@ -119,7 +119,7 @@ public class ValueAdapterRegistry {
|
||||
ValueType<T> type = ValueType.of(value);
|
||||
ValueAdapter<T> adapter = adapterOf(type);
|
||||
if (adapter == null) return value; // No adapters, try to return the original value
|
||||
return adapter.serialize(provider, type, value);
|
||||
return adapter.serialize(holder, type, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ 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;
|
||||
|
||||
@@ -12,12 +11,45 @@ import java.util.Objects;
|
||||
*/
|
||||
public abstract class ValueType<T> {
|
||||
|
||||
public static final ValueType<String> STRING = ofPrimitiveType(String.class);
|
||||
public static final ValueType<Integer> INTEGER = ofPrimitiveType(Integer.class);
|
||||
public static final ValueType<Integer> INTEGER_TYPE = ofPrimitiveType(int.class);
|
||||
public static final ValueType<Long> LONG = ofPrimitiveType(Long.class);
|
||||
public static final ValueType<Long> LONG_TYPE = ofPrimitiveType(long.class);
|
||||
public static final ValueType<Double> DOUBLE = ofPrimitiveType(Double.class);
|
||||
public static final ValueType<Double> DOUBLE_TYPE = ofPrimitiveType(double.class);
|
||||
public static final ValueType<Float> FLOAT = ofPrimitiveType(Float.class);
|
||||
public static final ValueType<Float> FLOAT_TYPE = ofPrimitiveType(float.class);
|
||||
public static final ValueType<Boolean> BOOLEAN = ofPrimitiveType(Boolean.class);
|
||||
public static final ValueType<Boolean> BOOLEAN_TYPE = ofPrimitiveType(boolean.class);
|
||||
public static final ValueType<Byte> BYTE = ofPrimitiveType(Byte.class);
|
||||
public static final ValueType<Byte> BYTE_TYPE = ofPrimitiveType(byte.class);
|
||||
public static final ValueType<Short> SHORT = ofPrimitiveType(Short.class);
|
||||
public static final ValueType<Short> SHORT_TYPE = ofPrimitiveType(short.class);
|
||||
public static final ValueType<Character> CHAR = ofPrimitiveType(Character.class);
|
||||
public static final ValueType<Character> 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 <T> ValueType<T> of(@NotNull T value) {
|
||||
return of((Class<T>) value.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> ValueType<T> 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<T>) valueType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ValueType<T>(type) {
|
||||
};
|
||||
}
|
||||
@@ -54,6 +86,11 @@ public abstract class ValueType<T> {
|
||||
return of(parameterizedType);
|
||||
}
|
||||
|
||||
private static <T> ValueType<T> ofPrimitiveType(Class<T> clazz) {
|
||||
return new ValueType<T>(clazz) {
|
||||
};
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
|
||||
protected ValueType() {
|
||||
@@ -139,6 +176,9 @@ public abstract class ValueType<T> {
|
||||
if (obj instanceof ValueType) {
|
||||
return Objects.equals(type, ((ValueType<?>) obj).type);
|
||||
}
|
||||
if (obj instanceof Type) {
|
||||
return Objects.equals(type, obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -2,16 +2,16 @@ 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.ConfigurationSection;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
|
||||
public interface StandardAdapters {
|
||||
|
||||
ValueAdapter<ConfigurationSection> SECTION_ADAPTER = new ValueAdapter<>(
|
||||
ValueType.of(ConfigurationSection.class),
|
||||
ValueAdapter<ConfigureSection> SECTION_ADAPTER = new ValueAdapter<>(
|
||||
ValueType.of(ConfigureSection.class),
|
||||
(provider, type, value) -> value,
|
||||
(provider, type, value) -> {
|
||||
if (value instanceof ConfigurationSection) {
|
||||
return (ConfigurationSection) value;
|
||||
if (value instanceof ConfigureSection) {
|
||||
return (ConfigureSection) value;
|
||||
} else throw new IllegalArgumentException("Value is not a ConfigurationSection");
|
||||
}
|
||||
);
|
||||
|
||||
+13
-13
@@ -5,7 +5,7 @@ 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.ConfigurationSection;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -21,39 +21,39 @@ public abstract class AbstractSectionBuilder<
|
||||
|
||||
protected final @NotNull ValueType<PARAM> paramType;
|
||||
|
||||
protected @NotNull ValueHandler<ConfigurationSection, PARAM> parser;
|
||||
protected @NotNull ValueHandler<PARAM, ? extends Map<Object, Object>> serializer;
|
||||
protected @NotNull ValueHandler<ConfigureSection, PARAM> parser;
|
||||
protected @NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer;
|
||||
|
||||
public AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<PARAM> paramType,
|
||||
@NotNull ValueHandler<ConfigurationSection, PARAM> parser,
|
||||
@NotNull ValueHandler<PARAM, ? extends Map<Object, Object>> serializer) {
|
||||
@NotNull ValueHandler<ConfigureSection, PARAM> parser,
|
||||
@NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
|
||||
super(type);
|
||||
this.paramType = paramType;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(DataFunction<ConfigurationSection, PARAM> valueParser) {
|
||||
public @NotNull SELF parse(DataFunction<ConfigureSection, PARAM> valueParser) {
|
||||
return parse((p, section) -> valueParser.handle(section));
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(ValueHandler<ConfigurationSection, PARAM> valueParser) {
|
||||
public @NotNull SELF parse(ValueHandler<ConfigureSection, PARAM> valueParser) {
|
||||
this.parser = valueParser;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(DataFunction<PARAM, ? extends Map<Object, Object>> serializer) {
|
||||
public @NotNull SELF serialize(DataFunction<PARAM, ? extends Map<String, Object>> serializer) {
|
||||
return serialize((p, value) -> serializer.handle(value));
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(ValueHandler<PARAM, ? extends Map<Object, Object>> serializer) {
|
||||
public @NotNull SELF serialize(ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
|
||||
this.serializer = serializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(Consumer<Map<Object, Object>> serializer) {
|
||||
public @NotNull SELF serialize(Consumer<Map<String, Object>> serializer) {
|
||||
return serialize((p, value) -> {
|
||||
Map<Object, Object> map = new LinkedHashMap<>();
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
serializer.accept(map);
|
||||
return map;
|
||||
});
|
||||
@@ -62,12 +62,12 @@ public abstract class AbstractSectionBuilder<
|
||||
protected ValueAdapter<PARAM> buildAdapter() {
|
||||
return new ValueAdapter<>(this.paramType)
|
||||
.parser((p, type, data) -> {
|
||||
ConfigurationSection section = p.deserialize(ConfigurationSection.class, data);
|
||||
ConfigureSection section = p.deserialize(ConfigureSection.class, data);
|
||||
if (section == null) return null;
|
||||
return this.parser.handle(p, section);
|
||||
})
|
||||
.serializer((p, type, data) -> {
|
||||
Map<Object, Object> map = this.serializer.handle(p, data);
|
||||
Map<String, Object> map = this.serializer.handle(p, data);
|
||||
return map == null || map.isEmpty() ? null : map;
|
||||
});
|
||||
}
|
||||
|
||||
+6
-6
@@ -49,13 +49,13 @@ public abstract class AbstractSourceBuilder<
|
||||
|
||||
protected ValueAdapter<PARAM> buildAdapter() {
|
||||
return new ValueAdapter<>(this.paramType)
|
||||
.parser((p, type, data) -> {
|
||||
SOURCE source = p.deserialize(this.sourceType, data);
|
||||
return this.valueParser.handle(p, source);
|
||||
.parser((holder, type, data) -> {
|
||||
SOURCE source = holder.deserialize(this.sourceType, data);
|
||||
return this.valueParser.handle(holder, source);
|
||||
})
|
||||
.serializer((p, type, data) -> {
|
||||
SOURCE source = this.valueSerializer.handle(p, data);
|
||||
return p.serialize(source);
|
||||
.serializer((holder, type, data) -> {
|
||||
SOURCE source = this.valueSerializer.handle(holder, data);
|
||||
return holder.serialize(source);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public class ConfigListBuilder<V> {
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<String, V> fromString() {
|
||||
return from(String.class);
|
||||
return new SourceListBuilder<>(ValueType.STRING, type, ValueHandler.required(), ValueHandler.stringValue(), ArrayList::new);
|
||||
}
|
||||
|
||||
public @NotNull SectionListBuilder<V> fromSection() {
|
||||
|
||||
@@ -3,7 +3,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.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigurationSection;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
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 ValueHandler<ConfigurationSection, V> parser,
|
||||
@NotNull ValueHandler<V, ? extends Map<Object, Object>> serializer,
|
||||
@NotNull ValueHandler<ConfigureSection, V> parser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, 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.value;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigurationSection;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -30,7 +30,7 @@ public class ConfigValueBuilder<V> {
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<String, V> fromString() {
|
||||
return from(String.class);
|
||||
return from(ValueType.STRING, ValueHandler.required(), ValueHandler.stringValue());
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection() {
|
||||
@@ -38,8 +38,8 @@ public class ConfigValueBuilder<V> {
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection(
|
||||
@NotNull ValueHandler<ConfigurationSection, V> valueParser,
|
||||
@NotNull ValueHandler<V, ? extends Map<Object, Object>> valueSerializer
|
||||
@NotNull ValueHandler<ConfigureSection, V> valueParser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, Object>> valueSerializer
|
||||
) {
|
||||
return new SectionValueBuilder<>(this.type, valueParser, valueSerializer);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,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.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigurationSection;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
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 ValueHandler<ConfigurationSection, V> parser,
|
||||
@NotNull ValueHandler<V, ? extends Map<Object, Object>> serializer) {
|
||||
@NotNull ValueHandler<ConfigureSection, V> parser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer) {
|
||||
super(type, type, parser, serializer);
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,11 @@ public interface ValueHandler<T, R> {
|
||||
return ConfigurationHolder::serialize;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ValueHandler<T, String> stringValue() {
|
||||
return (provider, input) -> String.valueOf(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ValueHandler<Object, T> fromObject(ValueType<T> type) {
|
||||
return (provider, input) -> provider.deserialize(type, input);
|
||||
|
||||
@@ -1,46 +1,38 @@
|
||||
package cc.carm.lib.configuration.source;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
|
||||
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.ConfigurationSource;
|
||||
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 ConfigurationSource<SOURCE, ?>, PROVIDER extends ConfigurationHolder<SOURCE>, SELF> {
|
||||
|
||||
protected Function<PROVIDER, ConfigurationInitializer> loaderFunction = PROVIDER -> new ConfigurationInitializer();
|
||||
protected Consumer<ConfigurationInitializer> loaderConsumer = loader -> {
|
||||
};
|
||||
public abstract class ConfigurationFactory<
|
||||
SOURCE extends ConfigureSource<?, ?, SOURCE>,
|
||||
HOLDER extends ConfigurationHolder<SOURCE>,
|
||||
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 loader(Function<PROVIDER, ConfigurationInitializer> loaderFunction) {
|
||||
this.loaderFunction = loaderFunction;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF loader(ConfigurationInitializer loader) {
|
||||
return loader(PROVIDER -> loader);
|
||||
}
|
||||
|
||||
public SELF loader(Consumer<ConfigurationInitializer> loaderConsumer) {
|
||||
this.loaderConsumer = this.loaderConsumer.andThen(loaderConsumer);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF pathGenerator(PathGenerator generator) {
|
||||
return loader(loader -> {
|
||||
loader.pathGenerator(generator);
|
||||
});
|
||||
}
|
||||
|
||||
public SELF adapters(ValueAdapterRegistry adapters) {
|
||||
this.adapters = adapters;
|
||||
@@ -52,18 +44,37 @@ public abstract class ConfigurationFactory<SOURCE extends ConfigurationSource<SO
|
||||
return self();
|
||||
}
|
||||
|
||||
// public SELF adapter(@NotNull ValueAdapter<?, ?> adapter) {
|
||||
// return adapter(a -> a.register(adapter));
|
||||
// }
|
||||
//
|
||||
// public <T> SELF adapter(Class<T> clazz, @NotNull ValueAdapter<?, T> adapter) {
|
||||
// return adapter(a -> a.register(clazz, adapter));
|
||||
// }
|
||||
//
|
||||
// public <B, V> SELF adapter(Class<B> baseClass, Class<V> valueClass,
|
||||
// ConfigDataFunction<B, V> parser, ConfigDataFunction<V, B> serializer) {
|
||||
// return adapter(a -> a.register(baseClass, valueClass, parser, serializer));
|
||||
// }
|
||||
public <T> SELF adapter(@NotNull ValueAdapter<T> adapter) {
|
||||
return adapter(a -> a.register(adapter));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||
return adapter(a -> a.register(type, serializer));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueParser<T> parser) {
|
||||
return adapter(a -> a.register(type, parser));
|
||||
}
|
||||
|
||||
public <FROM, TO> SELF adapter(@NotNull Class<FROM> from, @NotNull Class<TO> to,
|
||||
@NotNull DataFunction<FROM, TO> parser,
|
||||
@NotNull DataFunction<TO, FROM> serializer) {
|
||||
return adapter(a -> a.register(from, to, parser, serializer));
|
||||
}
|
||||
|
||||
public <FROM, TO> SELF adapter(@NotNull ValueType<FROM> from, @NotNull ValueType<TO> to,
|
||||
@NotNull DataFunction<FROM, TO> parser,
|
||||
@NotNull DataFunction<TO, FROM> serializer) {
|
||||
return adapter(a -> a.register(from, to, parser, serializer));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer, @NotNull ValueParser<T> parser) {
|
||||
return adapter(a -> a.register(type, serializer, parser));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull Class<T> type, @NotNull ValueSerializer<T> serializer, @NotNull ValueParser<T> parser) {
|
||||
return adapter(ValueType.of(type), serializer, parser);
|
||||
}
|
||||
|
||||
public SELF options(ConfigurationOptionHolder options) {
|
||||
this.options = options;
|
||||
@@ -79,6 +90,29 @@ public abstract class ConfigurationFactory<SOURCE extends ConfigurationSource<SO
|
||||
return option(o -> o.set(option, value));
|
||||
}
|
||||
|
||||
public abstract @NotNull PROVIDER build();
|
||||
|
||||
public SELF initializer(ConfigurationInitializer initializer) {
|
||||
this.initializer = initializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF initializer(Consumer<ConfigurationInitializer> initializerConsumer) {
|
||||
initializerConsumer.accept(initializer);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF pathGenerator(PathGenerator generator) {
|
||||
return initializer(loader -> {
|
||||
loader.pathGenerator(generator);
|
||||
});
|
||||
}
|
||||
|
||||
public <M, A extends Annotation> SELF metaAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull ConfigurationMetadata<M> metadata,
|
||||
@NotNull Function<A, M> extractor) {
|
||||
return initializer(loader -> loader.registerAnnotation(annotation, metadata, extractor));
|
||||
}
|
||||
|
||||
public abstract @NotNull HOLDER build();
|
||||
|
||||
}
|
||||
|
||||
@@ -6,16 +6,15 @@ 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.ConfigurationSource;
|
||||
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;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ConfigurationHolder<S extends ConfigurationSource<S, ?>> {
|
||||
public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, SOURCE>> {
|
||||
|
||||
protected final @NotNull ValueAdapterRegistry adapters;
|
||||
protected final @NotNull ConfigurationOptionHolder options;
|
||||
@@ -23,8 +22,6 @@ public class ConfigurationHolder<S extends ConfigurationSource<S, ?>> {
|
||||
|
||||
protected final @NotNull ConfigurationInitializer initializer;
|
||||
|
||||
protected @Nullable S source;
|
||||
|
||||
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
|
||||
@NotNull ConfigurationOptionHolder options,
|
||||
@NotNull Map<String, ConfigurationMetaHolder> metadata,
|
||||
@@ -35,16 +32,14 @@ public class ConfigurationHolder<S extends ConfigurationSource<S, ?>> {
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public @NotNull S source() {
|
||||
return Objects.requireNonNull(source, "Source is not initialized");
|
||||
}
|
||||
public abstract @NotNull SOURCE config();
|
||||
|
||||
public void reload() throws Exception {
|
||||
source().reload();
|
||||
config().reload();
|
||||
}
|
||||
|
||||
public void save() throws Exception {
|
||||
source().save();
|
||||
config().save();
|
||||
}
|
||||
|
||||
public ConfigurationOptionHolder options() {
|
||||
@@ -83,7 +78,7 @@ public class ConfigurationHolder<S extends ConfigurationSource<S, ?>> {
|
||||
return adapters().serialize(this, value);
|
||||
}
|
||||
|
||||
public void load(Class<? extends Configuration> configClass) {
|
||||
public void initialize(Class<? extends Configuration> configClass) {
|
||||
try {
|
||||
initializer.initialize(this, configClass);
|
||||
} catch (Exception e) {
|
||||
@@ -91,7 +86,7 @@ public class ConfigurationHolder<S extends ConfigurationSource<S, ?>> {
|
||||
}
|
||||
}
|
||||
|
||||
public void load(@NotNull Configuration config) {
|
||||
public void initialize(@NotNull Configuration config) {
|
||||
try {
|
||||
initializer.initialize(this, config);
|
||||
} catch (Exception e) {
|
||||
@@ -99,7 +94,7 @@ public class ConfigurationHolder<S extends ConfigurationSource<S, ?>> {
|
||||
}
|
||||
}
|
||||
|
||||
public void load(@NotNull ValueManifest<?> value) {
|
||||
public void initialize(@NotNull ValueManifest<?> value) {
|
||||
value.holder(this);
|
||||
}
|
||||
|
||||
|
||||
+23
-23
@@ -70,49 +70,49 @@ public class ConfigurationInitializer {
|
||||
public <T, A extends Annotation> void registerAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull ConfigurationMetadata<T> metadata,
|
||||
@NotNull Function<A, T> extractor) {
|
||||
appendFieldInitializer((provider, path, field) -> {
|
||||
appendFieldInitializer((holder, path, field) -> {
|
||||
A data = field.getAnnotation(annotation);
|
||||
if (data == null) return;
|
||||
provider.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||
});
|
||||
appendClassInitializer((provider, path, clazz) -> {
|
||||
appendClassInitializer((holder, path, clazz) -> {
|
||||
A data = clazz.getAnnotation(annotation);
|
||||
if (data == null) return;
|
||||
provider.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public @Nullable String getFieldPath(ConfigurationHolder<?> holder, @Nullable String parentPath, @NotNull Field field) {
|
||||
return pathGenerator.getFieldPath(provider, parentPath, field);
|
||||
return pathGenerator.getFieldPath(holder, parentPath, field);
|
||||
}
|
||||
|
||||
public @Nullable String getClassPath(ConfigurationHolder<?> holder, @Nullable String parentPath,
|
||||
@NotNull Class<?> clazz, @Nullable Field clazzField) {
|
||||
return pathGenerator.getClassPath(provider, parentPath, clazz, clazzField);
|
||||
return pathGenerator.getClassPath(holder, parentPath, clazz, clazzField);
|
||||
}
|
||||
|
||||
public void initialize(ConfigurationHolder<?> holder, @NotNull Configuration config) throws Exception {
|
||||
initializeInstance(provider, config, null, null);
|
||||
if (provider.options().get(StandardOptions.SET_DEFAULTS)) provider.save();
|
||||
initializeInstance(holder, config, null, null);
|
||||
if (holder.options().get(StandardOptions.SET_DEFAULTS)) holder.save();
|
||||
}
|
||||
|
||||
public void initialize(ConfigurationHolder<?> holder, @NotNull Class<? extends Configuration> clazz) throws Exception {
|
||||
initializeStaticClass(provider, clazz, null, null);
|
||||
if (provider.options().get(StandardOptions.SET_DEFAULTS)) provider.save();
|
||||
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(provider, parentPath, root.getClass(), configField);
|
||||
String path = getClassPath(holder, parentPath, root.getClass(), configField);
|
||||
try {
|
||||
this.classInitializer.whenInitialize(provider, path, root.getClass());
|
||||
this.classInitializer.whenInitialize(holder, path, root.getClass());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(provider, root, field, path));
|
||||
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(holder, root, field, path));
|
||||
}
|
||||
|
||||
// 针对静态类的初始化方法
|
||||
@@ -121,22 +121,22 @@ public class ConfigurationInitializer {
|
||||
@NotNull Class<?> clazz, @Nullable String parentPath, @Nullable Field configField) {
|
||||
if (!Configuration.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类
|
||||
|
||||
String path = getClassPath(provider, parentPath, clazz, configField);
|
||||
String path = getClassPath(holder, parentPath, clazz, configField);
|
||||
|
||||
try {
|
||||
this.classInitializer.whenInitialize(provider, path, (Class<? extends Configuration>) clazz);
|
||||
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
initializeField(provider, clazz, field, path);
|
||||
initializeField(holder, clazz, field, path);
|
||||
}
|
||||
|
||||
if (provider.options().get(StandardOptions.LOAD_SUB_CLASSES)) {
|
||||
if (holder.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);
|
||||
initializeStaticClass(holder, classes[i], path, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -150,20 +150,20 @@ public class ConfigurationInitializer {
|
||||
if (object instanceof ConfigValue<?>) {
|
||||
// 目标是 ConfigValue 实例,进行具体的初始化注入
|
||||
ConfigValue<?> value = (ConfigValue<?>) object;
|
||||
String path = getFieldPath(provider, parent, field);
|
||||
String path = getFieldPath(holder, parent, field);
|
||||
if (path == null) return;
|
||||
value.initialize(provider, path);
|
||||
value.initialize(holder, path);
|
||||
try {
|
||||
this.fieldInitializer.whenInitialize(provider, path, field);
|
||||
this.fieldInitializer.whenInitialize(holder, path, field);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if (source instanceof Configuration && object instanceof Configuration) {
|
||||
// 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。
|
||||
initializeInstance(provider, (Configuration) object, parent, field);
|
||||
initializeInstance(holder, (Configuration) object, parent, field);
|
||||
} else if (source instanceof Class<?> && object instanceof Class<?>) {
|
||||
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
|
||||
initializeStaticClass(provider, (Class<?>) object, parent, field);
|
||||
initializeStaticClass(holder, (Class<?>) object, parent, field);
|
||||
}
|
||||
|
||||
// 以上判断实现以下规范:
|
||||
|
||||
@@ -40,8 +40,8 @@ public class PathGenerator {
|
||||
public @Nullable String getFieldPath(@NotNull ConfigurationHolder<?> holder,
|
||||
@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()));
|
||||
if (path == null) return link(holder, parentPath, false, field.getName()); // No annotation, use field name.
|
||||
else return link(holder, parentPath, path.root(), select(path.value(), field.getName()));
|
||||
}
|
||||
|
||||
public @Nullable String getClassPath(@NotNull ConfigurationHolder<?> holder,
|
||||
@@ -52,14 +52,14 @@ public class PathGenerator {
|
||||
// and use filed information.
|
||||
ConfigPath clazzPath = clazz.getAnnotation(ConfigPath.class);
|
||||
|
||||
if (clazzPath != null) return link(provider, parentPath, clazzPath.root(), clazzPath.value());
|
||||
if (clazzPath != null) return link(holder, parentPath, clazzPath.root(), clazzPath.value());
|
||||
if (clazzField == null) {
|
||||
return link(provider, parentPath, false, clazz.getSimpleName()); // No field, use class name.
|
||||
return link(holder, 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);
|
||||
if (fieldPath == null) return link(holder, parentPath, false, clazzField.getName());
|
||||
else return getFieldPath(holder, parentPath, clazzField);
|
||||
}
|
||||
|
||||
protected String select(String path, String defaultValue) {
|
||||
@@ -70,7 +70,7 @@ public class PathGenerator {
|
||||
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 ? covertPath(path) : parent + pathSeparator(provider) + covertPath(path);
|
||||
return root || parent == null ? covertPath(path) : parent + pathSeparator(holder) + covertPath(path);
|
||||
}
|
||||
|
||||
public static boolean isBlank(String path) {
|
||||
@@ -78,7 +78,7 @@ public class PathGenerator {
|
||||
}
|
||||
|
||||
public static char pathSeparator(ConfigurationHolder<?> holder) {
|
||||
return provider.options().get(StandardOptions.PATH_SEPARATOR);
|
||||
return holder.options().get(StandardOptions.PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,7 @@ public interface StandardOptions {
|
||||
* <br> if false, the values will be parsed when calling
|
||||
* {@link cc.carm.lib.configuration.value.ConfigValue#get()}
|
||||
* <br> if true, the values will be parsed when
|
||||
* {@link ConfigurationHolder#load(Configuration)}.
|
||||
* {@link ConfigurationHolder#initialize(Configuration)}.
|
||||
*/
|
||||
ConfigurationOption<Boolean> PRELOAD = of(false);
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
package cc.carm.lib.configuration.source.section;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class ConfigurationSource<SELF extends ConfigurationSource<SELF, ORIGINAL>, ORIGINAL>
|
||||
implements ConfigurationSection {
|
||||
|
||||
protected final @NotNull ConfigurationHolder<? extends SELF> holder;
|
||||
protected long lastUpdateMillis;
|
||||
|
||||
protected ConfigurationSource(@NotNull ConfigurationHolder<? extends SELF> holder,
|
||||
long lastUpdateMillis) {
|
||||
this.holder = holder;
|
||||
this.lastUpdateMillis = lastUpdateMillis;
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationHolder<? extends SELF> 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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
+9
-2
@@ -1,6 +1,7 @@
|
||||
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.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -8,7 +9,13 @@ import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public interface ConfigurationSection {
|
||||
public interface ConfigureSection {
|
||||
|
||||
@NotNull ConfigureSource<?, ?, ?> source();
|
||||
|
||||
default char separator() {
|
||||
return source().holder().options().get(StandardOptions.PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
default Set<String> getKeys(boolean deep) {
|
||||
@@ -33,7 +40,7 @@ public interface ConfigurationSection {
|
||||
boolean isSection(@NotNull String path);
|
||||
|
||||
@Nullable
|
||||
ConfigurationSection getSection(@NotNull String path);
|
||||
ConfigureSection getSection(@NotNull String path);
|
||||
|
||||
@Nullable Object get(@NotNull String path);
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
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<SECTION, ORIGINAL, SELF>>
|
||||
implements ConfigureSection {
|
||||
|
||||
protected final @NotNull ConfigurationHolder<? extends SELF> holder;
|
||||
protected long lastUpdateMillis;
|
||||
|
||||
protected ConfigureSource(@NotNull ConfigurationHolder<? extends SELF> holder, long lastUpdateMillis) {
|
||||
this.holder = holder;
|
||||
this.lastUpdateMillis = lastUpdateMillis;
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationHolder<? extends SELF> 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 Configuration section
|
||||
*/
|
||||
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 @NotNull Map<String, Object> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,7 @@ 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.ConfigurationSource;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -101,8 +101,8 @@ public class ValueManifest<T> {
|
||||
throw new IllegalStateException("Value does not have a provider.");
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationSource<?, ?> config() {
|
||||
return holder().source();
|
||||
public @NotNull ConfigureSource<?, ?, ?> config() {
|
||||
return holder().config();
|
||||
}
|
||||
|
||||
public ConfigurationMetaHolder metadata() {
|
||||
|
||||
@@ -4,7 +4,7 @@ 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.ConfigurationSection;
|
||||
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;
|
||||
@@ -58,7 +58,7 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
|
||||
// If the value is expired, we need to update it
|
||||
Map<K, V> map = createMap();
|
||||
|
||||
ConfigurationSection section = config().getSection(path());
|
||||
ConfigureSection section = config().getSection(path());
|
||||
if (section == null) return getDefaultFirst(map);
|
||||
|
||||
Set<String> keys = section.getKeys(false);
|
||||
|
||||
@@ -30,6 +30,11 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
|
||||
return of(ValueType.of(type), () -> null);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull Class<V> type, @NotNull V defaults) {
|
||||
return of(ValueType.of(type), () -> defaults);
|
||||
}
|
||||
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull Class<V> type, @NotNull Supplier<@Nullable V> defaultSupplier) {
|
||||
return of(ValueType.of(type), defaultSupplier);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ 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;
|
||||
@@ -28,10 +29,17 @@ public class AdaptTest {
|
||||
data -> Duration.between(LocalTime.now(), data)
|
||||
);
|
||||
|
||||
ConfigurationHolder<TestSource> provider = new ConfigurationHolder<>(
|
||||
new TestSource(), registry, new ConfigurationOptionHolder(),
|
||||
ConfigurationHolder<TestSource> provider = new ConfigurationHolder<TestSource>(
|
||||
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);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import cc.carm.lib.configuration.source.loader.PathGenerator;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NameTest {
|
||||
|
||||
@@ -6,6 +6,7 @@ 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;
|
||||
@@ -14,10 +15,17 @@ public class LoaderTest {
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
ConfigurationHolder<TestSource> provider = new ConfigurationHolder<>(
|
||||
new TestSource(), new ValueAdapterRegistry(), new ConfigurationOptionHolder(),
|
||||
ConfigurationHolder<TestSource> provider = new ConfigurationHolder<TestSource>(
|
||||
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);
|
||||
|
||||
+9
-5
@@ -1,6 +1,7 @@
|
||||
package cc.carm.lib.configuration.json;
|
||||
package cc.carm.test.config;
|
||||
|
||||
import cc.carm.lib.configuration.source.section.ConfigurationSection;
|
||||
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;
|
||||
|
||||
@@ -8,8 +9,11 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class JSONConfigurationSection implements ConfigurationSection {
|
||||
|
||||
public class TestSection implements ConfigureSection {
|
||||
@Override
|
||||
public @NotNull ConfigureSource<?, ?, ?> source() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
@@ -42,7 +46,7 @@ public class JSONConfigurationSection implements ConfigurationSection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigurationSection getSection(@NotNull String path) {
|
||||
public @Nullable ConfigureSection getSection(@NotNull String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
package cc.carm.test.config;
|
||||
|
||||
import cc.carm.lib.configuration.source.section.ConfigurationSection;
|
||||
import cc.carm.lib.configuration.source.section.ConfigurationSource;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class TestSource extends ConfigurationSource<TestSource, Map<String, String>> {
|
||||
public class TestSource extends ConfigureSource<TestSection, Map<String, String>, TestSource> {
|
||||
|
||||
public TestSource() {
|
||||
super(System.currentTimeMillis());
|
||||
|
||||
public TestSource(@NotNull ConfigurationHolder<? extends TestSource> holder, long lastUpdateMillis) {
|
||||
super(holder, lastUpdateMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,47 +34,12 @@ public class TestSource extends ConfigurationSource<TestSource, Map<String, Stri
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<String> getKeys(boolean deep) {
|
||||
public @NotNull TestSection section() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@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 null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSection(@NotNull String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigurationSection getSection(@NotNull String path) {
|
||||
public @NotNull ConfigureSource<?, ?, ?> source() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,13 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-feature-commentable</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package cc.carm.lib.configuration.demo;
|
||||
|
||||
import cc.carm.lib.configuration.source.Configuration;
|
||||
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;
|
||||
|
||||
@@ -3,9 +3,9 @@ package cc.carm.lib.configuration.demo.tests;
|
||||
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.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -14,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.initializer().initialize(holder, DemoConfiguration.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("----------------------------------------------------");
|
||||
|
||||
@@ -48,21 +52,21 @@ public class ConfigurationTest {
|
||||
System.out.println(DemoConfiguration.MODEL_TEST.get());
|
||||
DemoConfiguration.MODEL_TEST.set(TestModel.random());
|
||||
|
||||
System.out.println("> Test Maps:");
|
||||
DemoConfiguration.USERS.forEach((k, v) -> System.out.println(k + ": " + v));
|
||||
LinkedHashMap<Integer, UUID> 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<Integer, UUID> 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();
|
||||
|
||||
provider.initialize(TEST, true);
|
||||
provider.initialize(TEST);
|
||||
|
||||
System.out.println("> Test Inner value before:");
|
||||
System.out.println(TEST.INNER.INNER_VALUE.getNotNull());
|
||||
@@ -76,7 +80,7 @@ public class ConfigurationTest {
|
||||
|
||||
}
|
||||
|
||||
public static void save(ConfigurationProvider<?> provider) {
|
||||
public static void save(ConfigurationHolder<?> provider) {
|
||||
try {
|
||||
provider.save();
|
||||
} catch (Exception e) {
|
||||
|
||||
+14
-16
@@ -1,15 +1,13 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.source.Configuration;
|
||||
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.TestModel;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredMap;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredSection;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
||||
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Objects;
|
||||
@@ -37,22 +35,22 @@ public interface DemoConfiguration extends Configuration {
|
||||
@ConfigPath("user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
||||
@InlineComment("Section数据也支持InlineComment注释")
|
||||
ConfigValue<TestModel> MODEL_TEST = ConfiguredSection.builderOf(TestModel.class)
|
||||
ConfigValue<TestModel> MODEL_TEST = ConfiguredValue.builderOf(TestModel.class).fromSection()
|
||||
.defaults(new TestModel("Carm", UUID.randomUUID()))
|
||||
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
|
||||
.serializeValue(TestModel::serialize).build();
|
||||
.parse((holder, section) -> TestModel.deserialize(section))
|
||||
.serialize(TestModel::serialize).build();
|
||||
|
||||
@HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"})
|
||||
ConfiguredMap<Integer, UUID> 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<Integer, UUID> 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 implements Configuration {
|
||||
|
||||
@@ -60,14 +58,14 @@ public interface DemoConfiguration extends Configuration {
|
||||
@InlineComment("This is an inline comment")
|
||||
public static final ConfigValue<UUID> 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 implements Configuration {
|
||||
|
||||
public static final ConfiguredList<UUID> OPERATORS = ConfiguredList
|
||||
.builderOf(UUID.class).fromString()
|
||||
.parseValue(s -> Objects.requireNonNull(UUID.fromString(s)))
|
||||
.parse(s -> Objects.requireNonNull(UUID.fromString(s)))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.source.Configuration;
|
||||
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.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredSection;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@@ -15,16 +14,15 @@ public class TestConfiguration implements Configuration {
|
||||
|
||||
public final TestInnerConfiguration INNER = new TestInnerConfiguration();
|
||||
|
||||
public final ConfigValue<Double> CLASS_VALUE = ConfiguredValue.of(Double.class, 1.0D);
|
||||
public final ConfigValue<Double> CLASS_VALUE = ConfiguredValue.of(1.0D);
|
||||
|
||||
@ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
||||
@InlineComment("Section数据也支持InlineComment注释")
|
||||
public final ConfigValue<TestModel> TEST_MODEL = ConfiguredSection
|
||||
.builderOf(TestModel.class)
|
||||
public final ConfigValue<TestModel> TEST_MODEL = ConfiguredValue.builderOf(TestModel.class).fromSection()
|
||||
.defaults(new TestModel("Carm", UUID.randomUUID()))
|
||||
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
|
||||
.serializeValue(TestModel::serialize).build();
|
||||
.parse((holder, section) -> TestModel.deserialize(section))
|
||||
.serialize((holder, data) -> data.serialize()).build();
|
||||
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.source.Configuration;
|
||||
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;
|
||||
@@ -8,6 +8,6 @@ import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
@HeaderComment("Inner Test")
|
||||
public class TestInnerConfiguration implements Configuration {
|
||||
|
||||
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(Double.class, 1.0D);
|
||||
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(1.0D);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package cc.carm.lib.configuration.demo.tests.model;
|
||||
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -32,7 +33,7 @@ public class TestModel extends AbstractModel {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static TestModel deserialize(ConfigurationWrapper<?> section) {
|
||||
public static TestModel deserialize(ConfigureSection section) {
|
||||
String name = section.getString("name");
|
||||
if (name == null) throw new NullPointerException("name is null");
|
||||
String uuidString = section.getString("info.uuid");
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package cc.carm.lib.configuration.source;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public abstract class FileConfigFactory<SOURCE extends FileConfigSource<?, ?, SOURCE>,
|
||||
HOLDER extends ConfigurationHolder<SOURCE>, SELF extends FileConfigFactory<SOURCE, HOLDER, SELF>>
|
||||
extends ConfigurationFactory<SOURCE, HOLDER, SELF> {
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
+11
-14
@@ -2,7 +2,8 @@ package cc.carm.lib.configuration.source;
|
||||
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.option.FileConfigOptions;
|
||||
import cc.carm.lib.configuration.source.section.ConfigurationSource;
|
||||
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;
|
||||
|
||||
@@ -14,8 +15,8 @@ import java.nio.file.Files;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public abstract class FileConfigSource<SELF extends FileConfigSource<SELF, ORIGINAL>, ORIGINAL>
|
||||
extends ConfigurationSource<SELF, ORIGINAL> {
|
||||
public abstract class FileConfigSource<SECTION extends ConfigureSection, ORIGINAL, SELF extends FileConfigSource<SECTION, ORIGINAL, SELF>>
|
||||
extends ConfigureSource<SECTION, ORIGINAL, SELF> {
|
||||
|
||||
protected final @NotNull File file;
|
||||
protected final @Nullable String resourcePath;
|
||||
@@ -27,6 +28,7 @@ public abstract class FileConfigSource<SELF extends FileConfigSource<SELF, ORIGI
|
||||
this.resourcePath = resourcePath;
|
||||
}
|
||||
|
||||
|
||||
public Charset charset() {
|
||||
return holder().options().get(FileConfigOptions.CHARSET);
|
||||
}
|
||||
@@ -56,28 +58,23 @@ public abstract class FileConfigSource<SELF extends FileConfigSource<SELF, ORIGI
|
||||
}
|
||||
}
|
||||
|
||||
protected <R> R fileInputStream(@NotNull DataFunction<InputStream, R> loader) {
|
||||
try (FileInputStream is = new FileInputStream(file)) {
|
||||
protected <R> R fileInputStream(@NotNull DataFunction<InputStream, R> loader) throws Exception {
|
||||
try (InputStream is = Files.newInputStream(file.toPath())) {
|
||||
return loader.handle(is);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected <R> R fileReader(@NotNull DataFunction<Reader, R> loader) {
|
||||
protected <R> R fileReader(@NotNull DataFunction<Reader, R> loader) throws Exception {
|
||||
return fileInputStream(is -> loader.handle(new InputStreamReader(is, charset())));
|
||||
}
|
||||
|
||||
protected void fileOutputStream(@NotNull Consumer<FileOutputStream> stream) {
|
||||
try (FileOutputStream os = new FileOutputStream(file)) {
|
||||
protected void fileOutputStream(@NotNull Consumer<OutputStream> stream) throws Exception {
|
||||
try (OutputStream os = Files.newOutputStream(file.toPath())) {
|
||||
stream.accept(os);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected void fileWriter(@NotNull Consumer<Writer> writer) {
|
||||
protected void fileWriter(@NotNull Consumer<Writer> writer) throws Exception {
|
||||
fileOutputStream(os -> writer.accept(new OutputStreamWriter(os, charset())));
|
||||
}
|
||||
|
||||
|
||||
@@ -21,11 +21,12 @@
|
||||
<module>features/commentable</module>
|
||||
<module>features/file</module>
|
||||
|
||||
<!-- <module>demo</module>-->
|
||||
<!-- <module>providers/yaml</module>-->
|
||||
<!-- <module>providers/yaml</module>-->
|
||||
<module>providers/gson</module>
|
||||
<!-- <module>providers/sql</module>-->
|
||||
<!-- <module>providers/hocon</module>-->
|
||||
<!-- <module>providers/sql</module>-->
|
||||
<!-- <module>providers/hocon</module>-->
|
||||
|
||||
<module>demo</module>
|
||||
</modules>
|
||||
|
||||
<name>EasyConfiguration</name>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package cc.carm.lib.configuration.json;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.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<JSONSource, ConfigurationHolder<JSONSource>, 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<Gson> gsonSupplier = () -> JSONSource.DEFAULT_GSON;
|
||||
|
||||
public JSONConfigFactory(@NotNull File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONConfigFactory self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSONConfigFactory gson(@NotNull Supplier<Gson> gsonSupplier) {
|
||||
this.gsonSupplier = gsonSupplier;
|
||||
return self();
|
||||
}
|
||||
|
||||
public JSONConfigFactory gson(@NotNull Gson gson) {
|
||||
return gson(() -> gson);
|
||||
}
|
||||
|
||||
public JSONConfigFactory gson(@NotNull Consumer<GsonBuilder> builder) {
|
||||
return gson(() -> {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
builder.accept(gsonBuilder);
|
||||
return gsonBuilder.create();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigurationHolder<JSONSource> 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<JSONSource>(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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package cc.carm.lib.configuration.json;
|
||||
|
||||
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||
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<JSONConfigWrapper> {
|
||||
|
||||
|
||||
|
||||
protected JSONConfigWrapper configuration;
|
||||
protected ConfigInitializer<JSONConfigProvider> 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<? extends ConfigurationProvider<JSONConfigWrapper>> getInitializer() {
|
||||
return this.initializer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
package cc.carm.lib.configuration.json;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.FileConfigSource;
|
||||
import cc.carm.lib.configuration.source.section.ConfigurationSection;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class JSONConfigSource extends FileConfigSource<JSONConfigSource, JsonObject> {
|
||||
|
||||
public static final @NotNull Gson DEFAULT_GSON = new GsonBuilder()
|
||||
.serializeNulls().disableHtmlEscaping().setPrettyPrinting()
|
||||
.registerTypeAdapter(
|
||||
JSONConfigWrapper.class,
|
||||
(JsonSerializer<JSONConfigWrapper>) (src, typeOfSrc, context) -> context.serialize(src.data)
|
||||
).create();
|
||||
|
||||
protected final @NotNull Gson gson;
|
||||
|
||||
protected @Nullable JsonObject original;
|
||||
|
||||
protected JSONConfigSource(@NotNull ConfigurationHolder<? extends JSONConfigSource> holder, long lastUpdateMillis,
|
||||
@NotNull File file, @Nullable String resourcePath, @NotNull Gson gson) {
|
||||
super(holder, lastUpdateMillis, file, resourcePath);
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
try {
|
||||
initializeFile();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void initializeJson() {
|
||||
this.original = fileReader(reader -> gson.fromJson(reader, JsonObject.class));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONConfigSource self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull JsonObject original() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReload() throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> 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 ConfigurationSection getSection(@NotNull String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+32
-32
@@ -1,31 +1,27 @@
|
||||
package cc.carm.lib.configuration.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.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Some code comes from BungeeCord's implementation of the JsonConfiguration.
|
||||
*
|
||||
* @author md_5, CarmJos
|
||||
*/
|
||||
public class JSONConfigWrapper implements ConfigurationWrapper<Map<String, Object>> {
|
||||
public class JSONSection implements ConfigureSection {
|
||||
|
||||
private static final char SEPARATOR = '.';
|
||||
protected final ConfigureSource<? extends JSONSection, ?, ?> source;
|
||||
protected final Map<String, Object> data;
|
||||
|
||||
JSONConfigWrapper(Map<?, ?> map) {
|
||||
public JSONSection(@NotNull ConfigureSource<? extends JSONSection, ?, ?> source,
|
||||
@NotNull Map<?, ?> data) {
|
||||
this.source = source;
|
||||
this.data = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
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 JSONConfigWrapper((Map<?, ?>) entry.getValue()));
|
||||
this.data.put(key, new JSONSection(source, (Map<?, ?>) entry.getValue()));
|
||||
} else {
|
||||
this.data.put(key, entry.getValue());
|
||||
}
|
||||
@@ -33,7 +29,11 @@ public class JSONConfigWrapper implements ConfigurationWrapper<Map<String, Objec
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getSource() {
|
||||
public @NotNull ConfigureSource<? extends JSONSection, ?, ?> source() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
public Map<String, Object> data() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@@ -54,10 +54,10 @@ public class JSONConfigWrapper implements ConfigurationWrapper<Map<String, Objec
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
if (value instanceof Map) {
|
||||
value = new JSONConfigWrapper((Map<?, ?>) value);
|
||||
value = new JSONSection(source(), (Map<?, ?>) value);
|
||||
}
|
||||
|
||||
JSONConfigWrapper section = getSectionFor(path);
|
||||
JSONSection section = getSectionFor(path);
|
||||
if (section == this) {
|
||||
if (value == null) {
|
||||
this.data.remove(path);
|
||||
@@ -76,7 +76,7 @@ public class JSONConfigWrapper implements ConfigurationWrapper<Map<String, Objec
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
JSONConfigWrapper section = getSectionFor(path);
|
||||
JSONSection section = getSectionFor(path);
|
||||
return section == this ? data.get(path) : section.get(getChild(path));
|
||||
}
|
||||
|
||||
@@ -92,44 +92,44 @@ public class JSONConfigWrapper implements ConfigurationWrapper<Map<String, Objec
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurationSection(@NotNull String path) {
|
||||
return get(path) instanceof JSONConfigWrapper;
|
||||
public boolean isSection(@NotNull String path) {
|
||||
return get(path) instanceof JSONSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable JSONConfigWrapper getConfigurationSection(@NotNull String path) {
|
||||
public @Nullable ConfigureSection getSection(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof JSONConfigWrapper) ? (JSONConfigWrapper) val : null;
|
||||
return (val instanceof ConfigureSection) ? (ConfigureSection) val : null;
|
||||
}
|
||||
|
||||
private JSONConfigWrapper getSectionFor(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
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 JSONConfigWrapper(new LinkedHashMap<>());
|
||||
section = new JSONSection(source(), new LinkedHashMap<>());
|
||||
this.data.put(root, section);
|
||||
}
|
||||
|
||||
return (JSONConfigWrapper) section;
|
||||
return (JSONSection) section;
|
||||
}
|
||||
|
||||
private String getChild(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
int index = path.indexOf(separator());
|
||||
return (index == -1) ? path : path.substring(index + 1);
|
||||
}
|
||||
|
||||
|
||||
protected void mapChildrenValues(@NotNull Map<String, Object> output, @NotNull JSONConfigWrapper section,
|
||||
protected void mapChildrenValues(@NotNull Map<String, Object> output, @NotNull JSONSection section,
|
||||
@Nullable String parent, boolean deep) {
|
||||
for (Map.Entry<String, Object> entry : section.data.entrySet()) {
|
||||
String path = (parent == null ? "" : parent + ".") + entry.getKey();
|
||||
for (Map.Entry<String, Object> 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 JSONConfigWrapper) {
|
||||
this.mapChildrenValues(output, (JSONConfigWrapper) entry.getValue(), path, true);
|
||||
if (deep && entry.getValue() instanceof JSONSection) {
|
||||
this.mapChildrenValues(output, (JSONSection) entry.getValue(), path, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package cc.carm.lib.configuration.json;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.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<JSONSection, Map<?, ?>, JSONSource> {
|
||||
|
||||
public static final @NotNull Gson DEFAULT_GSON = new GsonBuilder()
|
||||
.serializeNulls().disableHtmlEscaping().setPrettyPrinting()
|
||||
.registerTypeAdapter(
|
||||
JSONSection.class,
|
||||
(JsonSerializer<JSONSection>) (src, typeOfSrc, context) -> context.serialize(src.data)
|
||||
).create();
|
||||
|
||||
protected final @NotNull Gson gson;
|
||||
protected @Nullable JSONSection section;
|
||||
|
||||
protected JSONSource(@NotNull ConfigurationHolder<? extends JSONSource> holder, long lastUpdateMillis,
|
||||
@NotNull File file, @Nullable String resourcePath) {
|
||||
this(holder, lastUpdateMillis, file, resourcePath, DEFAULT_GSON);
|
||||
}
|
||||
|
||||
protected JSONSource(@NotNull ConfigurationHolder<? extends JSONSource> 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.section, "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.section = new JSONSection(this, data);
|
||||
this.lastUpdateMillis = System.currentTimeMillis(); // 更新时间
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,26 @@
|
||||
package config;
|
||||
|
||||
import cc.carm.lib.configuration.EasyConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||
import cc.carm.lib.configuration.json.JSONConfigProvider;
|
||||
import cc.carm.lib.configuration.json.JSONConfigFactory;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class JSONConfigTest {
|
||||
|
||||
protected final JSONConfigProvider provider = EasyConfiguration.from("target/config.json", "config.json");
|
||||
|
||||
protected final ConfigurationHolder<?> holder = JSONConfigFactory
|
||||
.from(new File("target"), "config.json")
|
||||
.option(StandardOptions.PATH_SEPARATOR, '-')
|
||||
.build();
|
||||
|
||||
@Test
|
||||
public void onTest() {
|
||||
ConfigurationTest.testDemo(this.holder);
|
||||
ConfigurationTest.testInner(this.holder);
|
||||
|
||||
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);
|
||||
ConfigurationTest.save(this.holder);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user