From 45ca8b02d405c9678382ba659ba5153483aba707 Mon Sep 17 00:00:00 2001 From: Carm Date: Sun, 9 Feb 2025 03:49:37 +0800 Subject: [PATCH] feat!(core): Finished the design of core module --- .../adapter/ValueAdapterRegistry.java | 10 +- ...iveAdapters.java => PrimitiveAdapter.java} | 72 ++++--- .../configuration/annotation/ConfigPath.java | 2 - .../builder/AbstractConfigBuilder.java | 34 +++- .../builder/impl/AbstractSectionBuilder.java | 20 +- .../builder/impl/AbstractSourceBuilder.java | 20 +- .../builder/list/ConfigListBuilder.java | 6 +- .../builder/list/SectionListBuilder.java | 6 +- .../builder/list/SourceListBuilder.java | 4 +- .../builder/value/ConfigValueBuilder.java | 14 +- .../builder/value/SectionValueBuilder.java | 6 +- .../builder/value/SourceValueBuilder.java | 4 +- ...figDataFunction.java => DataFunction.java} | 34 ++-- ...figValueHandler.java => ValueHandler.java} | 16 +- .../lib/configuration/meta/PathMetadata.java | 58 ------ .../source/ConfigurationFactory.java | 16 +- .../source/ConfigurationProvider.java | 130 ++----------- .../loader/ConfigInitializeHandler.java | 32 ++++ .../loader/ConfigurationInitializer.java | 177 ++++++++++++++++++ .../source/loader/ConfigurationLoader.java | 133 ------------- .../source/loader/PathGenerator.java | 82 +++++++- .../source/loader/StandardPathGenerator.java | 86 --------- .../source/meta/ConfigurationMetaHolder.java | 124 ++++++++++++ .../source/meta/ConfigurationMetadata.java | 57 ++++++ .../option/ConfigurationOptionHolder.java | 10 +- ...ationOptions.java => StandardOptions.java} | 2 +- .../source/section/ConfigurationSection.java | 40 ++-- .../lib/configuration/value/ConfigValue.java | 16 +- .../configuration/value/ValueManifest.java | 54 ++++-- core/src/test/java/AdaptTest.java | 15 +- core/src/test/java/NameTest.java | 1 - .../java/cc/carm/test/config/LoaderTest.java | 11 +- .../commentable/CommentableMetaTypes.java | 23 ++- .../commentable/ConfigurationComments.java | 48 ----- .../configuration/source/FileProvider.java | 8 +- 35 files changed, 758 insertions(+), 613 deletions(-) rename core/src/main/java/cc/carm/lib/configuration/adapter/strandard/{PrimitiveAdapters.java => PrimitiveAdapter.java} (52%) rename core/src/main/java/cc/carm/lib/configuration/function/{ConfigDataFunction.java => DataFunction.java} (77%) rename core/src/main/java/cc/carm/lib/configuration/function/{ConfigValueHandler.java => ValueHandler.java} (70%) delete mode 100644 core/src/main/java/cc/carm/lib/configuration/meta/PathMetadata.java create mode 100644 core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigInitializeHandler.java create mode 100644 core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationInitializer.java delete mode 100644 core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationLoader.java delete mode 100644 core/src/main/java/cc/carm/lib/configuration/source/loader/StandardPathGenerator.java create mode 100644 core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetaHolder.java create mode 100644 core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetadata.java rename core/src/main/java/cc/carm/lib/configuration/source/option/{ConfigurationOptions.java => StandardOptions.java} (96%) delete mode 100644 features/commentable/src/main/java/cc/carm/lib/configuration/commentable/ConfigurationComments.java diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapterRegistry.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapterRegistry.java index 2864261..d03eba4 100644 --- a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapterRegistry.java +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapterRegistry.java @@ -1,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> adapters = new HashSet<>(); public void register(@NotNull Class from, @NotNull Class to, - @Nullable ConfigDataFunction parser, - @Nullable ConfigDataFunction serializer) { + @Nullable DataFunction parser, + @Nullable DataFunction serializer) { register(ValueType.of(from), ValueType.of(to), parser, serializer); } public void register(@NotNull ValueType from, @NotNull ValueType to, - @Nullable ConfigDataFunction parser, - @Nullable ConfigDataFunction serializer) { + @Nullable DataFunction parser, + @Nullable DataFunction serializer) { ValueAdapter fromAdapter = adapterOf(from); if (fromAdapter == null) throw new IllegalArgumentException("No adapter for type " + from); register(to, diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapters.java b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapter.java similarity index 52% rename from core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapters.java rename to core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapter.java index 1017820..ecd3dd5 100644 --- a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapters.java +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/PrimitiveAdapter.java @@ -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 extends ValueAdapter { +import java.util.Arrays; - public static final PrimitiveAdapters[] ADAPTERS = new PrimitiveAdapters[]{ +public class PrimitiveAdapter extends ValueAdapter { + + public static final PrimitiveAdapter[] ADAPTERS = new PrimitiveAdapter[]{ ofString(), ofBoolean(), ofBooleanType(), ofCharacter(), ofCharacterType(), ofInteger(), ofIntegerType(), ofLong(), ofLongType(), ofDouble(), ofDoubleType(), ofFloat(), ofFloatType(), ofShort(), ofShortType(), ofByte(), ofByteType() }; + public static final String[] TRUE_VALUES = new String[]{ + "true", "yes", "on", "1", "enabled", "enable", "active" + }; + + public static final String[] FALSE_VALUES = new String[]{ + "false", "no", "off", "0", "disabled", "disable", "inactive" + }; + @SuppressWarnings({"unchecked", "rawtypes"}) public static ValueAdapter> ofEnum() { ValueAdapter> adapter = new ValueAdapter<>(new ValueType>() { @@ -23,86 +33,92 @@ public class PrimitiveAdapters extends ValueAdapter { return adapter; } - public static PrimitiveAdapters ofString() { + public static PrimitiveAdapter ofString() { return of(String.class, o -> o instanceof String ? (String) o : o.toString()); } - public static PrimitiveAdapters ofBoolean() { - return of(Boolean.class, o -> o instanceof Boolean ? (Boolean) o : Boolean.parseBoolean(o.toString())); + public static PrimitiveAdapter ofBoolean() { + return of(Boolean.class, data -> { + if (data instanceof Boolean) return (Boolean) data; + String v = data.toString().trim(); + if (Arrays.stream(TRUE_VALUES).anyMatch(v::equalsIgnoreCase)) return true; + else if (Arrays.stream(FALSE_VALUES).anyMatch(v::equalsIgnoreCase)) return false; + else throw new IllegalArgumentException("Cannot parse boolean from " + data); + }); } - public static PrimitiveAdapters ofBooleanType() { + public static PrimitiveAdapter ofBooleanType() { return of(boolean.class, o -> o instanceof Boolean ? (Boolean) o : Boolean.parseBoolean(o.toString())); } - public static PrimitiveAdapters ofCharacter() { + public static PrimitiveAdapter ofCharacter() { return of(Character.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0)); } - public static PrimitiveAdapters ofCharacterType() { + public static PrimitiveAdapter ofCharacterType() { return of(char.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0)); } - public static PrimitiveAdapters ofInteger() { + public static PrimitiveAdapter ofInteger() { return ofNumber(Integer.class, Number::intValue, Integer::parseInt); } - public static PrimitiveAdapters ofIntegerType() { + public static PrimitiveAdapter ofIntegerType() { return ofNumber(int.class, Number::intValue, Integer::parseInt); } - public static PrimitiveAdapters ofLong() { + public static PrimitiveAdapter ofLong() { return ofNumber(Long.class, Number::longValue, Long::parseLong); } - public static PrimitiveAdapters ofLongType() { + public static PrimitiveAdapter ofLongType() { return ofNumber(long.class, Number::longValue, Long::parseLong); } - public static PrimitiveAdapters ofDouble() { + public static PrimitiveAdapter ofDouble() { return ofNumber(Double.class, Number::doubleValue, Double::parseDouble); } - public static PrimitiveAdapters ofDoubleType() { + public static PrimitiveAdapter ofDoubleType() { return ofNumber(double.class, Number::doubleValue, Double::parseDouble); } - public static PrimitiveAdapters ofFloat() { + public static PrimitiveAdapter ofFloat() { return ofNumber(Float.class, Number::floatValue, Float::parseFloat); } - public static PrimitiveAdapters ofFloatType() { + public static PrimitiveAdapter ofFloatType() { return ofNumber(float.class, Number::floatValue, Float::parseFloat); } - public static PrimitiveAdapters ofShort() { + public static PrimitiveAdapter ofShort() { return ofNumber(Short.class, Number::shortValue, Short::parseShort); } - public static PrimitiveAdapters ofShortType() { + public static PrimitiveAdapter ofShortType() { return ofNumber(short.class, Number::shortValue, Short::parseShort); } - public static PrimitiveAdapters ofByte() { + public static PrimitiveAdapter ofByte() { return ofNumber(Byte.class, Number::byteValue, Byte::parseByte); } - public static PrimitiveAdapters ofByteType() { + public static PrimitiveAdapter ofByteType() { return ofNumber(byte.class, Number::byteValue, Byte::parseByte); } - public static PrimitiveAdapters of(@NotNull Class clazz, - @NotNull ConfigDataFunction function) { - return new PrimitiveAdapters<>(clazz, (p, type, data) -> function.handle(data)); + public static PrimitiveAdapter of(@NotNull Class clazz, + @NotNull DataFunction function) { + return new PrimitiveAdapter<>(clazz, (p, type, data) -> function.handle(data)); } - public static PrimitiveAdapters ofNumber(@NotNull Class numberClass, - @NotNull ConfigDataFunction castFunction, - @NotNull ConfigDataFunction parseFunction) { + public static PrimitiveAdapter ofNumber(@NotNull Class numberClass, + @NotNull DataFunction castFunction, + @NotNull DataFunction parseFunction) { return of(numberClass, o -> o instanceof Number ? castFunction.handle((Number) o) : parseFunction.handle(o.toString())); } - protected PrimitiveAdapters(@NotNull Class valueType, @NotNull ValueParser deserializer) { + protected PrimitiveAdapter(@NotNull Class valueType, @NotNull ValueParser deserializer) { super(ValueType.of(valueType), (provider, type, value) -> value, deserializer); } diff --git a/core/src/main/java/cc/carm/lib/configuration/annotation/ConfigPath.java b/core/src/main/java/cc/carm/lib/configuration/annotation/ConfigPath.java index 9d21c85..e8810f8 100644 --- a/core/src/main/java/cc/carm/lib/configuration/annotation/ConfigPath.java +++ b/core/src/main/java/cc/carm/lib/configuration/annotation/ConfigPath.java @@ -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; diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/AbstractConfigBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/AbstractConfigBuilder.java index 1d466de..cf59996 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/AbstractConfigBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/AbstractConfigBuilder.java @@ -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 defaultValueSupplier = () -> null; + protected @NotNull BiConsumer, String> initializer = (provider, path) -> { + }; protected AbstractConfigBuilder(Class providerClass, ValueType 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, String> initializer) { + this.initializer = initializer; + return self(); + } + + public @NotNull SELF append(@NotNull BiConsumer, String> initializer) { + return initializer(initializer.andThen(initializer)); + } + + public @NotNull SELF append(@NotNull Consumer> initializer) { + return append((provider, path) -> initializer.accept(provider)); + } + public @NotNull SELF defaults(@Nullable TYPE defaultValue) { return defaults(() -> defaultValue); } @@ -54,8 +73,19 @@ public abstract class AbstractConfigBuilder< return self(); } + public @NotNull SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) { + return append((provider, path) -> metaConsumer.accept(provider.metadata(path))); + } + + public @NotNull SELF meta(@NotNull ConfigurationMetadata type, @Nullable M value) { + return meta(holder -> holder.set(type, value)); + } + protected @NotNull ValueManifest buildManifest() { - return new ValueManifest<>(type(), this.provider, this.path, this.defaultValueSupplier); + return new ValueManifest<>( + type(), this.defaultValueSupplier, this.initializer, + this.provider, this.path + ); } } diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSectionBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSectionBuilder.java index 91879d3..e647a1e 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSectionBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSectionBuilder.java @@ -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 paramType; - protected @NotNull ConfigValueHandler parser; - protected @NotNull ConfigValueHandler> serializer; + protected @NotNull ValueHandler parser; + protected @NotNull ValueHandler> serializer; public AbstractSectionBuilder(@NotNull ValueType type, @NotNull ValueType paramType, - @NotNull ConfigValueHandler parser, - @NotNull ConfigValueHandler> serializer) { + @NotNull ValueHandler parser, + @NotNull ValueHandler> serializer) { super(type); this.paramType = paramType; this.parser = parser; this.serializer = serializer; } - public @NotNull SELF parse(ConfigDataFunction valueParser) { + public @NotNull SELF parse(DataFunction valueParser) { return parse((p, section) -> valueParser.handle(section)); } - public @NotNull SELF parse(ConfigValueHandler valueParser) { + public @NotNull SELF parse(ValueHandler valueParser) { this.parser = valueParser; return self(); } - public @NotNull SELF serialize(ConfigDataFunction> serializer) { + public @NotNull SELF serialize(DataFunction> serializer) { return serialize((p, value) -> serializer.handle(value)); } - public @NotNull SELF serialize(ConfigValueHandler> serializer) { + public @NotNull SELF serialize(ValueHandler> serializer) { this.serializer = serializer; return self(); } diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSourceBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSourceBuilder.java index a9830d3..2ec78b0 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSourceBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSourceBuilder.java @@ -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 sourceType; protected final @NotNull ValueType paramType; - protected @NotNull ConfigValueHandler valueParser; - protected @NotNull ConfigValueHandler valueSerializer; + protected @NotNull ValueHandler valueParser; + protected @NotNull ValueHandler valueSerializer; public AbstractSourceBuilder(@NotNull ValueType type, @NotNull ValueType sourceType, @NotNull ValueType paramType, - @NotNull ConfigValueHandler parser, - @NotNull ConfigValueHandler serializer) { + @NotNull ValueHandler parser, + @NotNull ValueHandler 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 parser) { + public @NotNull SELF parse(DataFunction parser) { return parse((p, source) -> parser.handle(source)); } - public @NotNull SELF parse(@NotNull ConfigValueHandler parser) { + public @NotNull SELF parse(@NotNull ValueHandler parser) { this.valueParser = parser; return self(); } - public @NotNull SELF serialize(@NotNull ConfigValueHandler serializer) { + public @NotNull SELF serialize(@NotNull ValueHandler serializer) { this.valueSerializer = serializer; return self(); } - public @NotNull SELF serialize(@NotNull ConfigDataFunction serializer) { + public @NotNull SELF serialize(@NotNull DataFunction serializer) { return serialize((p, value) -> serializer.handle(value)); } diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/list/ConfigListBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/list/ConfigListBuilder.java index cd7e8f1..e78082c 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/list/ConfigListBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/list/ConfigListBuilder.java @@ -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 { } public @NotNull SourceListBuilder from(@NotNull ValueType 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 fromString() { @@ -27,7 +27,7 @@ public class ConfigListBuilder { } public @NotNull SectionListBuilder fromSection() { - return new SectionListBuilder<>(type, ConfigValueHandler.required(), ConfigValueHandler.required(), ArrayList::new); + return new SectionListBuilder<>(type, ValueHandler.required(), ValueHandler.required(), ArrayList::new); } diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/list/SectionListBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/list/SectionListBuilder.java index d808a01..929cfe9 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/list/SectionListBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/list/SectionListBuilder.java @@ -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 extends AbstractSectionBuilder, V, Co protected @NotNull Supplier> constructor; public SectionListBuilder(@NotNull ValueType paramType, - @NotNull ConfigValueHandler parser, - @NotNull ConfigValueHandler> serializer, + @NotNull ValueHandler parser, + @NotNull ValueHandler> serializer, @NotNull Supplier> constructor) { super(new ValueType>() { }, paramType, parser, serializer); diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/list/SourceListBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/list/SourceListBuilder.java index bb73610..655114c 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/list/SourceListBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/list/SourceListBuilder.java @@ -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 protected @NotNull Supplier> constructor; public SourceListBuilder(@NotNull ValueType sourceType, @NotNull ValueType paramType, - @NotNull ConfigValueHandler parser, @NotNull ConfigValueHandler serializer, + @NotNull ValueHandler parser, @NotNull ValueHandler serializer, @NotNull Supplier> constructor) { super(new ValueType>() { }, sourceType, paramType, parser, serializer); diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/value/ConfigValueBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/value/ConfigValueBuilder.java index b9447d0..c3ee264 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/value/ConfigValueBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/value/ConfigValueBuilder.java @@ -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 { } public @NotNull SourceValueBuilder from(@NotNull ValueType sourceType) { - return from(sourceType, ConfigValueHandler.required(), ConfigValueHandler.required()); + return from(sourceType, ValueHandler.required(), ValueHandler.required()); } public @NotNull SourceValueBuilder from(@NotNull ValueType sourceType, - @NotNull ConfigValueHandler valueParser, - @NotNull ConfigValueHandler valueSerializer) { + @NotNull ValueHandler valueParser, + @NotNull ValueHandler valueSerializer) { return new SourceValueBuilder<>(sourceType, this.type, valueParser, valueSerializer); } @@ -34,12 +34,12 @@ public class ConfigValueBuilder { } public @NotNull SectionValueBuilder fromSection() { - return fromSection(ConfigValueHandler.required(), ConfigValueHandler.required()); + return fromSection(ValueHandler.required(), ValueHandler.required()); } public @NotNull SectionValueBuilder fromSection( - @NotNull ConfigValueHandler valueParser, - @NotNull ConfigValueHandler> valueSerializer + @NotNull ValueHandler valueParser, + @NotNull ValueHandler> valueSerializer ) { return new SectionValueBuilder<>(this.type, valueParser, valueSerializer); } diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/value/SectionValueBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/value/SectionValueBuilder.java index 5d6577a..52019ee 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/value/SectionValueBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/value/SectionValueBuilder.java @@ -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 extends AbstractSectionBuilder, SectionValueBuilder> { public SectionValueBuilder(@NotNull ValueType type, - @NotNull ConfigValueHandler parser, - @NotNull ConfigValueHandler> serializer) { + @NotNull ValueHandler parser, + @NotNull ValueHandler> serializer) { super(type, type, parser, serializer); } diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/value/SourceValueBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/value/SourceValueBuilder.java index ee0e943..96b01b0 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/value/SourceValueBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/value/SourceValueBuilder.java @@ -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 extends AbstractSourceBuilder sourceType, @NotNull ValueType valueType, - @NotNull ConfigValueHandler parser, @NotNull ConfigValueHandler serializer) { + @NotNull ValueHandler parser, @NotNull ValueHandler serializer) { super(valueType, sourceType, valueType, parser, serializer); } diff --git a/core/src/main/java/cc/carm/lib/configuration/function/ConfigDataFunction.java b/core/src/main/java/cc/carm/lib/configuration/function/DataFunction.java similarity index 77% rename from core/src/main/java/cc/carm/lib/configuration/function/ConfigDataFunction.java rename to core/src/main/java/cc/carm/lib/configuration/function/DataFunction.java index 7571bcf..75a4708 100644 --- a/core/src/main/java/cc/carm/lib/configuration/function/ConfigDataFunction.java +++ b/core/src/main/java/cc/carm/lib/configuration/function/DataFunction.java @@ -7,39 +7,39 @@ import org.jetbrains.annotations.NotNull; import java.util.Objects; @FunctionalInterface -public interface ConfigDataFunction { +public interface DataFunction { @NotNull R handle(@NotNull T data) throws Exception; - default @NotNull ConfigDataFunction andThen(@NotNull ConfigDataFunction after) { + default @NotNull DataFunction andThen(@NotNull DataFunction after) { Objects.requireNonNull(after); return data -> after.handle(handle(data)); } @Contract(pure = true) - static @NotNull ConfigDataFunction identity() { + static @NotNull DataFunction identity() { return input -> input; } @Contract(pure = true) - static @NotNull ConfigDataFunction identity(Class type) { + static @NotNull DataFunction identity(Class type) { return input -> input; } @Contract(pure = true) - static @NotNull ConfigDataFunction required() { + static @NotNull DataFunction required() { return input -> { throw new IllegalArgumentException("Please specify the value parser."); }; } @Contract(pure = true) - static @NotNull ConfigDataFunction toObject() { + static @NotNull DataFunction toObject() { return input -> input; } @Contract(pure = true) - static @NotNull ConfigDataFunction castObject(Class valueClass) { + static @NotNull DataFunction castObject(Class valueClass) { return input -> { if (valueClass.isInstance(input)) return valueClass.cast(input); else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName()); @@ -47,7 +47,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction castFromString(Class valueClass) { + static @NotNull DataFunction castFromString(Class valueClass) { return input -> { if (valueClass.isInstance(input)) return valueClass.cast(input); else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName()); @@ -55,7 +55,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction castToString() { + static @NotNull DataFunction castToString() { return input -> { if (input instanceof String) return (String) input; else if (input instanceof Enum) return ((Enum) input).name(); @@ -64,7 +64,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction parseString(Class valueClass) { + static @NotNull DataFunction parseString(Class valueClass) { return input -> { if (valueClass.isInstance(input)) return valueClass.cast(input); else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName()); @@ -72,7 +72,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction intValue() { + static @NotNull DataFunction intValue() { return input -> { if (input instanceof Integer) { return (Integer) input; @@ -83,7 +83,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction shortValue() { + static @NotNull DataFunction shortValue() { return input -> { if (input instanceof Short) { return (Short) input; @@ -94,7 +94,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction doubleValue() { + static @NotNull DataFunction doubleValue() { return input -> { if (input instanceof Double) { return (Double) input; @@ -105,7 +105,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction byteValue() { + static @NotNull DataFunction byteValue() { return input -> { if (input instanceof Byte) { return (Byte) input; @@ -116,7 +116,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction floatValue() { + static @NotNull DataFunction floatValue() { return input -> { if (input instanceof Float) { return (Float) input; @@ -127,7 +127,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction longValue() { + static @NotNull DataFunction longValue() { return input -> { if (input instanceof Long) { return (Long) input; @@ -138,7 +138,7 @@ public interface ConfigDataFunction { } @Contract(pure = true) - static @NotNull ConfigDataFunction booleanValue() { + static @NotNull DataFunction booleanValue() { return input -> { if (input instanceof Boolean) { return (Boolean) input; diff --git a/core/src/main/java/cc/carm/lib/configuration/function/ConfigValueHandler.java b/core/src/main/java/cc/carm/lib/configuration/function/ValueHandler.java similarity index 70% rename from core/src/main/java/cc/carm/lib/configuration/function/ConfigValueHandler.java rename to core/src/main/java/cc/carm/lib/configuration/function/ValueHandler.java index 165f471..9f24b8d 100644 --- a/core/src/main/java/cc/carm/lib/configuration/function/ConfigValueHandler.java +++ b/core/src/main/java/cc/carm/lib/configuration/function/ValueHandler.java @@ -10,11 +10,11 @@ import org.jetbrains.annotations.Nullable; import java.util.Objects; @FunctionalInterface -public interface ConfigValueHandler { +public interface ValueHandler { @Nullable R handle(@NotNull ConfigurationProvider provider, @NotNull T data) throws Exception; - default ConfigValueHandler andThen(@NotNull ConfigValueHandler after) { + default ValueHandler andThen(@NotNull ValueHandler after) { Objects.requireNonNull(after); return ((provider, data) -> { R result = handle(provider, data); @@ -23,7 +23,7 @@ public interface ConfigValueHandler { }); } - default ConfigValueHandler compose(@NotNull ConfigValueHandler before) { + default ValueHandler compose(@NotNull ValueHandler before) { Objects.requireNonNull(before); return ((provider, data) -> { T result = before.handle(provider, data); @@ -32,7 +32,7 @@ public interface ConfigValueHandler { }); } - default ConfigValueHandler compose(@NotNull ConfigDataFunction before) { + default ValueHandler compose(@NotNull DataFunction before) { Objects.requireNonNull(before); return ((provider, data) -> { T result = before.handle(data); @@ -41,23 +41,23 @@ public interface ConfigValueHandler { } @Contract(pure = true) - static @NotNull ConfigValueHandler identity() { + static @NotNull ValueHandler identity() { return (provider, input) -> input; } @Contract(pure = true) - static @NotNull ConfigValueHandler toObject() { + static @NotNull ValueHandler toObject() { return ConfigurationProvider::serialize; } @Contract(pure = true) - static @NotNull ConfigValueHandler fromObject(ValueType type) { + static @NotNull ValueHandler fromObject(ValueType type) { return (provider, input) -> provider.deserialize(type, input); } @Contract(pure = true) - static @NotNull ConfigValueHandler required() { + static @NotNull ValueHandler required() { return (provider, input) -> { throw new IllegalArgumentException("Please specify the value parser."); }; diff --git a/core/src/main/java/cc/carm/lib/configuration/meta/PathMetadata.java b/core/src/main/java/cc/carm/lib/configuration/meta/PathMetadata.java deleted file mode 100644 index a2df1b1..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/meta/PathMetadata.java +++ /dev/null @@ -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 { - - public static PathMetadata of() { - return of(() -> null); - } - - public static PathMetadata of(T defaults) { - return of(() -> defaults); - } - - public static PathMetadata of(@NotNull Supplier<@Nullable T> defaults) { - return of(v -> defaults.get()); - } - - public static PathMetadata of(@NotNull Function defaults) { - return new PathMetadata<>(defaults); - } - - protected Function defaultFunction; - - public PathMetadata(@NotNull Function 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 defaultFunction) { - this.defaultFunction = defaultFunction; - } - - public void setDefaults(T value) { - setDefaults((v) -> value); - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationFactory.java b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationFactory.java index 3046267..23d734e 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationFactory.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationFactory.java @@ -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, PROVIDER extends ConfigurationProvider, SELF> { - protected Function loaderFunction = PROVIDER -> new ConfigurationLoader(); - protected Consumer loaderConsumer = loader -> { + protected Function loaderFunction = PROVIDER -> new ConfigurationInitializer(); + protected Consumer loaderConsumer = loader -> { }; protected ValueAdapterRegistry adapters = new ValueAdapterRegistry(); @@ -22,23 +22,23 @@ public abstract class ConfigurationFactory loaderFunction) { + public SELF loader(Function loaderFunction) { this.loaderFunction = loaderFunction; return self(); } - public SELF loader(ConfigurationLoader loader) { + public SELF loader(ConfigurationInitializer loader) { return loader(PROVIDER -> loader); } - public SELF loader(Consumer loaderConsumer) { + public SELF loader(Consumer 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); }); } diff --git a/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationProvider.java b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationProvider.java index 68f7097..9749984 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationProvider.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationProvider.java @@ -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> { protected final @NotNull S source; - protected final @NotNull ConfigurationLoader loader; protected final @NotNull ValueAdapterRegistry adapters; protected final @NotNull ConfigurationOptionHolder options; - protected final @NotNull Map, Object>> pathMetadata; + protected final @NotNull Map 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, Object>> pathMetadata) { + @NotNull Map 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> { return options; } - public @NotNull T option(@NotNull ConfigurationOption option) { - return options.get(option); + public @NotNull Map metadata() { + return this.metadata; } - public void option(@NotNull ConfigurationOption 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, Object>> pathMetadata() { - return pathMetadata; - } - - public @NotNull Map, 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 Value type - * @return Value of option - */ - @SuppressWarnings("unchecked") - @Contract("_,_, !null -> !null") - public @Nullable V meta(@NotNull String path, - @NotNull PathMetadata type, @Nullable V defaultValue) { - return (V) metadata(path).getOrDefault(type, type.getDefault(path, defaultValue)); - } - - /** - * Get the value of option. - * - * @param type {@link PathMetadata} - * @param Value type - * @return Value of option - */ - public @Nullable V meta(@NotNull String path, @NotNull PathMetadata 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. - *
Will only be changed in current holder. - * - * @param type {@link PathMetadata} - * @param value Value of meta - * @param Value type - * @return Previous value of meta - */ - @SuppressWarnings("unchecked") - public @Nullable V setMeta(@NotNull String path, @NotNull PathMetadata 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. - *
Will only be changed in current holder. - * - * @param type {@link PathMetadata} - * @param value Value of meta - * @param Value type - */ - public void setMetaIfAbsent(@NotNull String path, @NotNull PathMetadata 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. - *
Will only be changed in current holder. - * - * @param type {@link PathMetadata} - * @param value Value of meta - * @param Value type - */ - @SuppressWarnings("unchecked") - public @Nullable V setMetaIfPresent(@NotNull String path, @NotNull PathMetadata 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> { public T deserialize(@NotNull ValueType type, @Nullable Object source) throws Exception { return adapters().deserialize(this, type, source); } - + @Contract("null -> null") public Object serialize(@Nullable T value) throws Exception { return adapters().serialize(this, value); @@ -175,7 +85,7 @@ public class ConfigurationProvider> { public void load(Class configClass) { try { - loader.load(this, configClass); + initializer.initialize(this, configClass); } catch (Exception e) { e.printStackTrace(); } @@ -183,7 +93,7 @@ public class ConfigurationProvider> { public void load(@NotNull Configuration config) { try { - loader.load(this, config); + initializer.initialize(this, config); } catch (Exception e) { e.printStackTrace(); } diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigInitializeHandler.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigInitializeHandler.java new file mode 100644 index 0000000..2ffe1b8 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigInitializeHandler.java @@ -0,0 +1,32 @@ +package cc.carm.lib.configuration.source.loader; + +import cc.carm.lib.configuration.source.ConfigurationProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface ConfigInitializeHandler { + + static ConfigInitializeHandler start() { + return (provider, path, value) -> { + }; + } + + void whenInitialize(@NotNull ConfigurationProvider provider, @Nullable String path, @NotNull T value) throws Exception; + + default ConfigInitializeHandler andThen(ConfigInitializeHandler after) { + return (provider, path, value) -> { + whenInitialize(provider, path, value); + after.whenInitialize(provider, path, value); + }; + } + + default ConfigInitializeHandler compose(ConfigInitializeHandler before) { + return (provider, path, value) -> { + before.whenInitialize(provider, path, value); + whenInitialize(provider, path, value); + }; + } + + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationInitializer.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationInitializer.java new file mode 100644 index 0000000..2463752 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationInitializer.java @@ -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 fieldInitializer; + protected @NotNull ConfigInitializeHandler> classInitializer; + + public ConfigurationInitializer() { + this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start()); + } + + public ConfigurationInitializer(@NotNull PathGenerator pathGenerator, + @NotNull ConfigInitializeHandler fieldInitializer, + @NotNull ConfigInitializeHandler> classInitializer) { + this.pathGenerator = pathGenerator; + this.fieldInitializer = fieldInitializer; + this.classInitializer = classInitializer; + } + + public void pathGenerator(@NotNull PathGenerator pathGenerator) { + this.pathGenerator = pathGenerator; + } + + public @NotNull PathGenerator pathGenerator() { + return pathGenerator; + } + + public ConfigInitializeHandler fieldInitializer() { + return fieldInitializer; + } + + public void fieldInitializer(@NotNull ConfigInitializeHandler fieldInitializer) { + this.fieldInitializer = fieldInitializer; + } + + public ConfigInitializeHandler> classInitializer() { + return classInitializer; + } + + public void classInitializer(@NotNull ConfigInitializeHandler> classInitializer) { + this.classInitializer = classInitializer; + } + + public void appendFieldInitializer(@NotNull ConfigInitializeHandler fieldInitializer) { + this.fieldInitializer = this.fieldInitializer.andThen(fieldInitializer); + } + + public void appendClassInitializer(@NotNull ConfigInitializeHandler> classInitializer) { + this.classInitializer = this.classInitializer.andThen(classInitializer); + } + + public void registerAnnotation(@NotNull Class annotation, + @NotNull ConfigurationMetadata metadata, + @NotNull Function extractor) { + appendFieldInitializer((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 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) 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) { + } + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationLoader.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationLoader.java deleted file mode 100644 index 7c4052f..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigurationLoader.java +++ /dev/null @@ -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 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) { - } - } - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java index f7d508e..0f2dd10 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java @@ -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 pathConverter) { + return new PathGenerator(pathConverter); + } + + protected UnaryOperator pathConverter; + + public PathGenerator(UnaryOperator pathConverter) { + this.pathConverter = pathConverter; + } + + public @NotNull UnaryOperator getPathConverter() { + return pathConverter; + } + + public void setPathConverter(UnaryOperator pathConverter) { + this.pathConverter = pathConverter; + } + + public String covertPath(String name) { + return pathConverter.apply(name); + } + + public @Nullable String getFieldPath(@NotNull 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. + *

+ * 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(); } + } diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/StandardPathGenerator.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/StandardPathGenerator.java deleted file mode 100644 index 2612e0a..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/source/loader/StandardPathGenerator.java +++ /dev/null @@ -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 pathConverter) { - return new StandardPathGenerator(pathConverter); - } - - protected UnaryOperator pathConverter; - - public StandardPathGenerator(UnaryOperator pathConverter) { - this.pathConverter = pathConverter; - } - - public @NotNull UnaryOperator getPathConverter() { - return pathConverter; - } - - public void setPathConverter(UnaryOperator pathConverter) { - this.pathConverter = pathConverter; - } - - public String covertPath(String name) { - return pathConverter.apply(name); - } - - public 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); - } - - -} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetaHolder.java b/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetaHolder.java new file mode 100644 index 0000000..fce23be --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetaHolder.java @@ -0,0 +1,124 @@ +package cc.carm.lib.configuration.source.meta; + +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class ConfigurationMetaHolder { + + protected final @NotNull Map, Object> values; + + public ConfigurationMetaHolder() { + this(new ConcurrentHashMap<>()); + } + + public ConfigurationMetaHolder(@NotNull Map, Object> values) { + this.values = values; + } + + public @NotNull Map, Object> values() { + return values; + } + + /** + * Get the value of option. + * + * @param type {@link ConfigurationMetadata} + * @param defaultValue Default value if the value of option is not set. + * @param Value type + * @return Value of option + */ + @SuppressWarnings("unchecked") + @Contract("_,!null -> !null") + public @Nullable V get(@NotNull ConfigurationMetadata type, @Nullable V defaultValue) { + return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue)); + } + + /** + * Get the value of option. + * + * @param type {@link ConfigurationMetadata} + * @param defaultValue Default value if the value of option is not set. + * @param Value type + * @return Value of option + */ + @SuppressWarnings("unchecked") + @Contract("_,!null -> !null") + public @Nullable V get(@NotNull ConfigurationMetadata type, Supplier<@Nullable V> defaultValue) { + return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue)); + } + + /** + * Get the value of option. + * + * @param type {@link ConfigurationMetadata} + * @param Value type + * @return Value of option + */ + public @Nullable V get(@NotNull ConfigurationMetadata type) { + return get(type, (V) null); + } + + public boolean contains(@NotNull ConfigurationMetadata type) { + return values().containsKey(type) || type.hasDefaults(); + } + + /** + * Set the value of meta, if the value is null, the meta will be removed. + *
Will only be changed in current holder. + * + * @param type {@link ConfigurationMetadata} + * @param value Value of meta + * @param Value type + * @return Previous value of meta + */ + @SuppressWarnings("unchecked") + public @Nullable V set(@NotNull ConfigurationMetadata type, @Nullable V value) { + if (value == null || type.isDefault(value)) { + return (V) values().remove(type); + } else { + return (V) values().put(type, value); + } + } + + /** + * Set the value of meta, if the value is null, the meta will not be changed. + *
Will only be changed in current holder. + * + * @param type {@link ConfigurationMetadata} + * @param value Value of meta + * @param Value type + */ + public void setIfAbsent(@NotNull ConfigurationMetadata type, @Nullable V value) { + if (value == null || type.isDefault(value)) { + values().remove(type); + } else { + values().putIfAbsent(type, value); + } + } + + /** + * Set the value of meta, if the value is null, the meta will not be changed. + *
Will only be changed in current holder. + * + * @param type {@link ConfigurationMetadata} + * @param value Value of meta + * @param Value type + */ + @SuppressWarnings("unchecked") + public @Nullable V setIfPresent(@NotNull ConfigurationMetadata type, @Nullable V value) { + Object exists = values().get(type); + if (exists == null) return null; + + if (value == null || type.isDefault(value)) { + return (V) values().remove(type); + } else { + return (V) values().put(type, value); + } + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetadata.java b/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetadata.java new file mode 100644 index 0000000..ff28804 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/source/meta/ConfigurationMetadata.java @@ -0,0 +1,57 @@ +package cc.carm.lib.configuration.source.meta; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Supplier; + +public class ConfigurationMetadata { + + public static ConfigurationMetadata of() { + return of(() -> null); + } + + public static ConfigurationMetadata of(T defaults) { + return of(() -> defaults); + } + + public static ConfigurationMetadata of(@NotNull Supplier<@Nullable T> defaults) { + return new ConfigurationMetadata<>(defaults); + } + + protected Supplier<@Nullable T> defaultsSupplier; + + public ConfigurationMetadata(@NotNull Supplier<@Nullable T> defaults) { + this.defaultsSupplier = defaults; + } + + public boolean isDefault(@NotNull T value) { + return value.equals(defaults()); + } + + public boolean hasDefaults() { + return defaults() != null; + } + + public T defaultOrSupply(@Nullable T suppliedValue) { + return defaultOrSupply(() -> suppliedValue); + } + + public T defaultOrSupply(Supplier<@Nullable T> suppliedValue) { + T defaults = defaults(); + return defaults == null ? suppliedValue.get() : defaults; + } + + public @Nullable T defaults() { + return defaultsSupplier.get(); + } + + public void setDefaults(Supplier defaultFunction) { + this.defaultsSupplier = defaultFunction; + } + + public void setDefaults(T value) { + setDefaults(() -> value); + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptionHolder.java b/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptionHolder.java index abacb5d..00f5c5b 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptionHolder.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptionHolder.java @@ -9,7 +9,7 @@ import java.util.concurrent.ConcurrentHashMap; public class ConfigurationOptionHolder { - public static @NotNull ConfigurationOptionHolder of(@NotNull Map, V> options) { + public static @NotNull ConfigurationOptionHolder of(@NotNull Map, Object> options) { return new ConfigurationOptionHolder(new ConcurrentHashMap<>(options)); } @@ -23,7 +23,7 @@ public class ConfigurationOptionHolder { this.options = options; } - public @NotNull Map, Object> options() { + public @NotNull Map, Object> values() { return options; } @@ -36,7 +36,7 @@ public class ConfigurationOptionHolder { */ @SuppressWarnings("unchecked") public @NotNull V get(@NotNull ConfigurationOption 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 @Nullable V set(@NotNull ConfigurationOption 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); } } diff --git a/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptions.java b/core/src/main/java/cc/carm/lib/configuration/source/option/StandardOptions.java similarity index 96% rename from core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptions.java rename to core/src/main/java/cc/carm/lib/configuration/source/option/StandardOptions.java index 377a273..a14c641 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOptions.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/option/StandardOptions.java @@ -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. diff --git a/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigurationSection.java b/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigurationSection.java index 088c1d8..05a1afa 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigurationSection.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigurationSection.java @@ -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 get(@NotNull String path, @NotNull ConfigDataFunction parser) { + default @Nullable T get(@NotNull String path, @NotNull DataFunction parser) { return get(path, null, parser); } @Contract("_,!null,_->!null") default @Nullable T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class clazz) { - return get(path, defaultValue, ConfigDataFunction.castObject(clazz)); + return get(path, defaultValue, DataFunction.castObject(clazz)); } @Contract("_,!null,_->!null") default @Nullable T get(@NotNull String path, @Nullable T defaultValue, - @NotNull ConfigDataFunction parser) { + @NotNull DataFunction 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 @NotNull List getList(@NotNull String path, @NotNull ConfigDataFunction parser) { + default @NotNull List getList(@NotNull String path, @NotNull DataFunction parser) { return parseList(getList(path), parser); } @Unmodifiable default @NotNull List getStringList(@NotNull String path) { - return getList(path, ConfigDataFunction.castToString()); + return getList(path, DataFunction.castToString()); } @Unmodifiable default @NotNull List getIntegerList(@NotNull String path) { - return getList(path, ConfigDataFunction.intValue()); + return getList(path, DataFunction.intValue()); } @Unmodifiable default @NotNull List getLongList(@NotNull String path) { - return getList(path, ConfigDataFunction.longValue()); + return getList(path, DataFunction.longValue()); } @Unmodifiable default @NotNull List getDoubleList(@NotNull String path) { - return getList(path, ConfigDataFunction.doubleValue()); + return getList(path, DataFunction.doubleValue()); } @Unmodifiable default @NotNull List getFloatList(@NotNull String path) { - return getList(path, ConfigDataFunction.floatValue()); + return getList(path, DataFunction.floatValue()); } @Unmodifiable default @NotNull List getByteList(@NotNull String path) { - return getList(path, ConfigDataFunction.byteValue()); + return getList(path, DataFunction.byteValue()); } @Unmodifiable default @NotNull List getCharList(@NotNull String path) { - return getList(path, ConfigDataFunction.castObject(Character.class)); + return getList(path, DataFunction.castObject(Character.class)); } @Unmodifiable - static @NotNull List parseList(@Nullable List list, ConfigDataFunction parser) { + static @NotNull List parseList(@Nullable List list, DataFunction parser) { if (list == null) return Collections.emptyList(); List values = new ArrayList<>(); for (Object o : list) { diff --git a/core/src/main/java/cc/carm/lib/configuration/value/ConfigValue.java b/core/src/main/java/cc/carm/lib/configuration/value/ConfigValue.java index c198c18..05459b2 100644 --- a/core/src/main/java/cc/carm/lib/configuration/value/ConfigValue.java +++ b/core/src/main/java/cc/carm/lib/configuration/value/ConfigValue.java @@ -10,7 +10,7 @@ import java.util.Optional; public abstract class ConfigValue extends ValueManifest { protected ConfigValue(@NotNull ValueManifest manifest) { - super(manifest.type, manifest.provider, manifest.path, manifest.defaultSupplier); + super(manifest); } /** @@ -37,7 +37,7 @@ public abstract class ConfigValue extends ValueManifest { * @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 extends ValueManifest { return Objects.equals(defaults(), get()); } + /** + * Try to save the configuration. + *
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(); + } + } diff --git a/core/src/main/java/cc/carm/lib/configuration/value/ValueManifest.java b/core/src/main/java/cc/carm/lib/configuration/value/ValueManifest.java index f508cd7..6ccdd01 100644 --- a/core/src/main/java/cc/carm/lib/configuration/value/ValueManifest.java +++ b/core/src/main/java/cc/carm/lib/configuration/value/ValueManifest.java @@ -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 { protected final @NotNull ValueType 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 type) { - this(type, null, null, () -> null); + + public ValueManifest(@NotNull ValueType 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 type, @NotNull Supplier<@Nullable T> defaultSupplier) { - this(type, null, null, defaultSupplier); + this(type, defaultSupplier, EMPTY_INITIALIZER, null, null); } - public ValueManifest(@NotNull ValueType type, - @Nullable ConfigurationProvider provider, @Nullable String path, - @NotNull Supplier<@Nullable T> defaultSupplier) { + public ValueManifest(@NotNull ValueType type, @NotNull Supplier<@Nullable T> defaultSupplier, + @NotNull BiConsumer<@NotNull ConfigurationProvider, @NotNull String> initializer) { + this(type, defaultSupplier, initializer, null, null); + } + + public ValueManifest(@NotNull ValueType 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 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 type() { @@ -81,6 +105,10 @@ public class ValueManifest { return provider().source(); } + public ConfigurationMetaHolder metadata() { + return provider().metadata(path()); + } + protected Object getData() { return config().get(path()); } @@ -89,8 +117,8 @@ public class ValueManifest { config().set(path(), value); } - public Map, Object> metadata() { - return provider().metadata(path()); - } + + private static final @NotNull BiConsumer<@NotNull ConfigurationProvider, @NotNull String> EMPTY_INITIALIZER = (provider, path) -> { + }; } diff --git a/core/src/test/java/AdaptTest.java b/core/src/test/java/AdaptTest.java index e1ede3a..99dbace 100644 --- a/core/src/test/java/AdaptTest.java +++ b/core/src/test/java/AdaptTest.java @@ -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 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); diff --git a/core/src/test/java/NameTest.java b/core/src/test/java/NameTest.java index 604057c..9b4cce8 100644 --- a/core/src/test/java/NameTest.java +++ b/core/src/test/java/NameTest.java @@ -1,4 +1,3 @@ -import cc.carm.lib.configuration.source.loader.PathGenerator; import org.junit.Test; public class NameTest { diff --git a/core/src/test/java/cc/carm/test/config/LoaderTest.java b/core/src/test/java/cc/carm/test/config/LoaderTest.java index 7210546..b42e961 100644 --- a/core/src/test/java/cc/carm/test/config/LoaderTest.java +++ b/core/src/test/java/cc/carm/test/config/LoaderTest.java @@ -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 provider = new ConfigurationProvider<>(new TestSource(), new ConfigurationLoader(), new ValueAdapterRegistry(), new ConfigurationOptionHolder(), new ConcurrentHashMap<>()); + ConfigurationProvider 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 { diff --git a/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/CommentableMetaTypes.java b/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/CommentableMetaTypes.java index 866abcb..b18433e 100644 --- a/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/CommentableMetaTypes.java +++ b/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/CommentableMetaTypes.java @@ -2,8 +2,12 @@ package cc.carm.lib.configuration.commentable; import cc.carm.lib.configuration.annotation.HeaderComment; import cc.carm.lib.configuration.annotation.InlineComment; -import cc.carm.lib.configuration.meta.PathMetadata; +import cc.carm.lib.configuration.source.ConfigurationProvider; +import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; +import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; +import org.jetbrains.annotations.NotNull; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -12,11 +16,24 @@ public interface CommentableMetaTypes { /** * Configuration's {@link HeaderComment} */ - PathMetadata> HEADER_COMMENTS = PathMetadata.of(Collections.emptyList()); + ConfigurationMetadata> HEADER_COMMENTS = ConfigurationMetadata.of(Collections.emptyList()); /** * Configuration's {@link InlineComment} */ - PathMetadata INLINE_COMMENT_VALUE = PathMetadata.of(); + ConfigurationMetadata INLINE_COMMENT = ConfigurationMetadata.of(); + + + static void register(@NotNull ConfigurationProvider provider) { + register(provider.initializer()); + } + + static void register(@NotNull ConfigurationInitializer initializer) { + initializer.registerAnnotation( + HeaderComment.class, HEADER_COMMENTS, + a -> Arrays.asList(a.value()) + ); + initializer.registerAnnotation(InlineComment.class, INLINE_COMMENT, InlineComment::value); + } } diff --git a/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/ConfigurationComments.java b/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/ConfigurationComments.java deleted file mode 100644 index c7b9ba0..0000000 --- a/features/commentable/src/main/java/cc/carm/lib/configuration/commentable/ConfigurationComments.java +++ /dev/null @@ -1,48 +0,0 @@ -package cc.carm.lib.configuration.commentable; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; - -import java.util.*; - -public class ConfigurationComments { - - protected final @NotNull Map> headerComments = new HashMap<>(); - protected final @NotNull Map inlineComments = new HashMap<>(); - - protected @NotNull Map> getHeaderComments() { - return headerComments; - } - - protected @NotNull Map getInlineComments() { - return inlineComments; - } - - public void setHeaderComments(@Nullable String path, @Nullable List comments) { - if (comments == null) { - getHeaderComments().remove(path); - } else { - getHeaderComments().put(path, comments); - } - } - - public void setInlineComment(@NotNull String path, @Nullable String comment) { - if (comment == null) { - getInlineComments().remove(path); - } else { - getInlineComments().put(path, comment); - } - } - - @Nullable - @Unmodifiable - public List getHeaderComment(@Nullable String path) { - return Optional.ofNullable(getHeaderComments().get(path)).map(Collections::unmodifiableList).orElse(null); - } - - public @Nullable String getInlineComment(@NotNull String path) { - return getInlineComments().get(path); - } - -} diff --git a/features/file/src/main/java/cc/carm/lib/configuration/source/FileProvider.java b/features/file/src/main/java/cc/carm/lib/configuration/source/FileProvider.java index f10b3da..67cc8a8 100644 --- a/features/file/src/main/java/cc/carm/lib/configuration/source/FileProvider.java +++ b/features/file/src/main/java/cc/carm/lib/configuration/source/FileProvider.java @@ -1,8 +1,8 @@ package cc.carm.lib.configuration.source; import cc.carm.lib.configuration.adapter.ValueAdapterRegistry; -import cc.carm.lib.configuration.meta.PathMetadata; -import cc.carm.lib.configuration.source.loader.ConfigurationLoader; +import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; +import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder; import cc.carm.lib.configuration.source.section.ConfigurationSource; import org.jetbrains.annotations.NotNull; @@ -11,9 +11,9 @@ import java.util.Map; public abstract class FileProvider> extends ConfigurationProvider { - public FileProvider(@NotNull S source, @NotNull ConfigurationLoader loader, + public FileProvider(@NotNull S source, @NotNull ConfigurationInitializer loader, @NotNull ValueAdapterRegistry adapters, @NotNull ConfigurationOptionHolder options, - @NotNull Map, Object>> pathMetadata) { + @NotNull Map, Object>> pathMetadata) { super(source, loader, adapters, options, pathMetadata); }