From fae048dd6950ff62c0bc454aa18043c2896e183d Mon Sep 17 00:00:00 2001 From: carm Date: Sun, 16 Mar 2025 23:56:36 +0800 Subject: [PATCH] feat(validator): Support validators for config values. BREAKING CHANGE: `ValueManifest` and `ConfigValue` added a new type "UNIT" to mark the minimal unit value of this instance. link #132 --- core/pom.xml | 2 +- .../configuration/adapter/ValueAdapter.java | 9 +- .../adapter/ValueAdapterRegistry.java | 4 +- .../configuration/adapter/ValueParser.java | 3 +- .../adapter/ValueSerializer.java | 3 +- .../adapter/strandard/StandardAdapters.java | 8 ++ .../carm/lib/configuration/annotation/Ex.java | 21 ++++ .../builder/AbstractConfigBuilder.java | 106 +++++++++++++++--- .../builder/CommonConfigBuilder.java | 7 +- .../builder/impl/AbstractSectionBuilder.java | 34 +++--- .../builder/impl/AbstractSourceBuilder.java | 28 ++--- .../builder/list/SectionListBuilder.java | 12 +- .../builder/list/SourceListBuilder.java | 18 +++ .../builder/map/SectionMapBuilder.java | 5 +- .../builder/map/SourceMapBuilder.java | 5 +- .../builder/value/ConfigValueBuilder.java | 1 - .../builder/value/SourceValueBuilder.java | 3 +- .../configuration/function/DataValidator.java | 17 +++ .../configuration/function/ValueComposer.java | 29 +++++ .../configuration/function/ValueConsumer.java | 21 ---- .../configuration/function/ValueSupplier.java | 7 ++ .../function/ValueValidator.java | 47 ++++++++ .../source/ConfigurationFactory.java | 3 +- .../source/ConfigurationHolder.java | 4 +- .../loader/ConfigInitializeHandler.java | 2 +- .../loader/ConfigurationInitializer.java | 14 +-- .../source/meta/StandardMeta.java | 2 +- .../lib/configuration/value/ConfigValue.java | 4 +- .../configuration/value/ValueManifest.java | 70 ++++++++---- .../value/impl/CachedConfigValue.java | 4 +- .../value/standard/ConfiguredList.java | 36 ++++-- .../value/standard/ConfiguredMap.java | 18 ++- .../value/standard/ConfiguredValue.java | 60 ++++++++-- demo/pom.xml | 2 +- .../demo/tests/conf/DemoConfiguration.java | 12 +- .../demo/tests/conf/InstanceConfig.java | 3 +- .../demo/tests/conf/RegistryConfig.java | 3 +- features/commentable/pom.xml | 2 +- features/file/pom.xml | 2 +- features/section/pom.xml | 2 +- features/text/pom.xml | 2 +- features/validators/pom.xml | 51 +++++++++ .../annotation/ValuePattern.java | 16 +++ .../configuration/annotation/ValueRange.java | 18 +++ .../validators/StandardValidators.java | 8 ++ features/versioned/pom.xml | 2 +- pom.xml | 3 +- providers/gson/pom.xml | 2 +- providers/hocon/pom.xml | 2 +- providers/mongodb/pom.xml | 2 +- providers/sql/pom.xml | 2 +- providers/yaml/pom.xml | 2 +- 52 files changed, 577 insertions(+), 166 deletions(-) create mode 100644 core/src/main/java/cc/carm/lib/configuration/annotation/Ex.java create mode 100644 core/src/main/java/cc/carm/lib/configuration/function/DataValidator.java create mode 100644 core/src/main/java/cc/carm/lib/configuration/function/ValueComposer.java delete mode 100644 core/src/main/java/cc/carm/lib/configuration/function/ValueConsumer.java create mode 100644 core/src/main/java/cc/carm/lib/configuration/function/ValueSupplier.java create mode 100644 core/src/main/java/cc/carm/lib/configuration/function/ValueValidator.java create mode 100644 features/validators/pom.xml create mode 100644 features/validators/src/main/java/cc/carm/lib/configuration/annotation/ValuePattern.java create mode 100644 features/validators/src/main/java/cc/carm/lib/configuration/annotation/ValueRange.java create mode 100644 features/validators/src/main/java/cc/carm/lib/configuration/validators/StandardValidators.java diff --git a/core/pom.xml b/core/pom.xml index 7f1d213..d2d697c 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,7 @@ configured-parent cc.carm.lib - 4.1.0 + 4.1.1 4.0.0 diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapter.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapter.java index 9df8f70..d71d668 100644 --- a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapter.java +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueAdapter.java @@ -15,6 +15,7 @@ public class ValueAdapter implements ValueSerializer, ValueParser { protected final @NotNull ValueType type; + protected @Nullable ValueSerializer serializer; protected @Nullable ValueParser deserializer; @@ -53,13 +54,17 @@ public class ValueAdapter } @Override - public Object serialize(@NotNull ConfigurationHolder holder, @NotNull ValueType type, @NotNull TYPE value) throws Exception { + public @Nullable Object serialize( + @NotNull ConfigurationHolder holder, @NotNull ValueType type, + @NotNull TYPE value) throws Exception { if (serializer == null) throw new UnsupportedOperationException("Serializer is not supported"); return serializer.serialize(holder, type, value); } @Override - public TYPE parse(@NotNull ConfigurationHolder holder, @NotNull ValueType type, @NotNull Object value) throws Exception { + public @Nullable TYPE parse( + @NotNull ConfigurationHolder holder, @NotNull ValueType type, + @NotNull Object value) throws Exception { if (deserializer == null) throw new UnsupportedOperationException("Deserializer is not supported"); return deserializer.parse(holder, type, value); } 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 f15d6f2..f4823ab 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 @@ -61,7 +61,9 @@ public class ValueAdapterRegistry { } } - public void register(@NotNull ValueType type, @Nullable ValueSerializer serializer, @Nullable ValueParser deserializer) { + public void register(@NotNull ValueType type, + @Nullable ValueSerializer serializer, + @Nullable ValueParser deserializer) { if (serializer == null && deserializer == null) return; ValueAdapter existing = adapterOf(type); if (existing != null) { diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueParser.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueParser.java index e5162b4..271c00c 100644 --- a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueParser.java +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueParser.java @@ -2,6 +2,7 @@ package cc.carm.lib.configuration.adapter; import cc.carm.lib.configuration.source.ConfigurationHolder; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Value deserializer, convert base data to target value. @@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull; @FunctionalInterface public interface ValueParser { - TYPE parse( + @Nullable TYPE parse( @NotNull ConfigurationHolder holder, @NotNull ValueType type, @NotNull Object data ) throws Exception; diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueSerializer.java b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueSerializer.java index 37e74e0..5243e7f 100644 --- a/core/src/main/java/cc/carm/lib/configuration/adapter/ValueSerializer.java +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/ValueSerializer.java @@ -2,6 +2,7 @@ package cc.carm.lib.configuration.adapter; import cc.carm.lib.configuration.source.ConfigurationHolder; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Value serializer, convert target value to base data. @@ -11,7 +12,7 @@ import org.jetbrains.annotations.NotNull; @FunctionalInterface public interface ValueSerializer { - Object serialize( + @Nullable Object serialize( @NotNull ConfigurationHolder holder, @NotNull ValueType type, @NotNull TYPE value ) throws Exception; diff --git a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/StandardAdapters.java b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/StandardAdapters.java index a0d3435..473e444 100644 --- a/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/StandardAdapters.java +++ b/core/src/main/java/cc/carm/lib/configuration/adapter/strandard/StandardAdapters.java @@ -5,6 +5,8 @@ import cc.carm.lib.configuration.adapter.ValueType; import cc.carm.lib.configuration.source.section.ConfigureSection; import org.jetbrains.annotations.NotNull; +import java.util.UUID; + import static cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter.*; public interface StandardAdapters { @@ -17,6 +19,12 @@ public interface StandardAdapters { @NotNull ValueAdapter> ENUMS = PrimitiveAdapter.ofEnum(); + @NotNull ValueAdapter UUID = new ValueAdapter<>( + ValueType.of(UUID.class), + (provider, type, value) -> value.toString(), + (provider, type, value) -> java.util.UUID.fromString(value.toString()) + ); + @NotNull ValueAdapter SECTIONS = new ValueAdapter<>( ValueType.of(ConfigureSection.class), (provider, type, value) -> value, diff --git a/core/src/main/java/cc/carm/lib/configuration/annotation/Ex.java b/core/src/main/java/cc/carm/lib/configuration/annotation/Ex.java new file mode 100644 index 0000000..c21ad53 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/annotation/Ex.java @@ -0,0 +1,21 @@ +package cc.carm.lib.configuration.annotation; + +import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; + +public class Ex { + + static void init(ConfigurationInitializer initializer) { + initializer.appendFieldInitializer((holder, path, field, value) -> { + ValueRange range = field.getAnnotation(ValueRange.class); + if (range == null) return; + value.validate((h, v) -> { + if (!(v instanceof Number)) return; + Number number = (Number) v; + if (number.doubleValue() >= range.min() && number.doubleValue() <= range.max()) { + throw new IllegalArgumentException(range.message()); + } + }); + }); + } + +} 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 4f87719..e617190 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 @@ -1,21 +1,26 @@ package cc.carm.lib.configuration.builder; import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.function.DataValidator; +import cc.carm.lib.configuration.function.ValueValidator; import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder; import cc.carm.lib.configuration.source.meta.ConfigurationMetadata; import cc.carm.lib.configuration.value.ConfigValue; import cc.carm.lib.configuration.value.ValueManifest; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.NotNullByDefault; import org.jetbrains.annotations.Nullable; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.function.Supplier; +@NotNullByDefault public abstract class AbstractConfigBuilder< - TYPE, RESULT extends ConfigValue, HOLDER extends ConfigurationHolder, - SELF extends AbstractConfigBuilder + TYPE, UNIT, RESULT extends ConfigValue, HOLDER extends ConfigurationHolder, + SELF extends AbstractConfigBuilder > { protected final Class providerClass; @@ -24,6 +29,7 @@ public abstract class AbstractConfigBuilder< protected @Nullable HOLDER holder; protected @Nullable String path; + protected @NotNull ValueValidator valueValidator = ValueValidator.none(); protected @NotNull Supplier<@Nullable TYPE> defaultValueSupplier = () -> null; protected @NotNull BiConsumer, String> initializer = (h, p) -> { }; @@ -37,54 +43,124 @@ public abstract class AbstractConfigBuilder< return type; } - protected abstract @NotNull SELF self(); + protected abstract SELF self(); public abstract @NotNull RESULT build(); - public @NotNull SELF holder(@Nullable HOLDER holder) { + public SELF holder(@Nullable HOLDER holder) { this.holder = holder; return self(); } - public @NotNull SELF path(@Nullable String path) { + public SELF path(@Nullable String path) { this.path = path; return self(); } - public @NotNull SELF initializer(@NotNull BiConsumer, String> initializer) { + /** + * Set the {@link ValueValidator} for the value. + * + * @param validator The validator to set. + * @return this builder + */ + public SELF validator(@NotNull ValueValidator validator) { + this.valueValidator = validator; + return self(); + } + + /** + * Set the {@link DataValidator} for the value. + * + * @param validator The validator to set. + * @return this builder + */ + public SELF validator(@NotNull DataValidator validator) { + return validator((h, value) -> validator.validate(value)); + } + + /** + * Validate the value with the specified condition. + * + * @param validator The validator to set. + * @return this builder + */ + public SELF validate(@NotNull ValueValidator validator) { + return validator((h, v) -> { + this.valueValidator.validate(h, v); + validator.validate(h, v); + }); + } + + /** + * Validate the value with the specified condition. + * + * @param validator The validator to set. + * @return this builder + */ + public SELF validate(@NotNull DataValidator validator) { + return validate((h, value) -> validator.validate(value)); + } + + /** + * Validate the value with the specified condition. + * + * @param condition The condition to check, if the condition is false, an exception will be thrown. + * @param exception The exception to throw if the condition is false. + * @return this builder + */ + public SELF validate(@NotNull Predicate condition, @NotNull Exception exception) { + return validate((h, value) -> { + if (!condition.test(value)) throw exception; + }); + } + + /** + * Validate the value with the specified condition. + * + * @param condition The condition to check, if the condition is false, an exception will be thrown. + * @param msg The message to throw if the condition is false. + * @return this builder + */ + public SELF validate(@NotNull Predicate condition, @NotNull String msg) { + return validate((h, value) -> { + if (!condition.test(value)) throw new IllegalArgumentException(msg); + }); + } + + public SELF initializer(@NotNull BiConsumer, String> initializer) { this.initializer = initializer; return self(); } - public @NotNull SELF append(@NotNull BiConsumer, String> initializer) { + public SELF append(@NotNull BiConsumer, String> initializer) { return initializer(initializer.andThen(initializer)); } - public @NotNull SELF append(@NotNull Consumer> initializer) { + public SELF append(@NotNull Consumer> initializer) { return append((provider, valuePath) -> initializer.accept(provider)); } - public @NotNull SELF defaults(@Nullable TYPE defaultValue) { + public SELF defaults(@Nullable TYPE defaultValue) { return defaults(() -> defaultValue); } - public @NotNull SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) { + public SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) { this.defaultValueSupplier = supplier; return self(); } - public @NotNull SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) { + public SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) { return append((h, p) -> metaConsumer.accept(h.metadata(p))); } - public @NotNull SELF meta(@NotNull ConfigurationMetadata type, @Nullable M value) { + public SELF meta(@NotNull ConfigurationMetadata type, @Nullable M value) { return meta(h -> h.set(type, value)); } - protected @NotNull ValueManifest buildManifest() { + protected @NotNull ValueManifest buildManifest() { return new ValueManifest<>( - type(), this.defaultValueSupplier, this.initializer, - this.holder, this.path + type(), this.defaultValueSupplier, this.valueValidator, + this.initializer, this.holder, this.path ); } diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/CommonConfigBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/CommonConfigBuilder.java index 03dff8d..256334a 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/CommonConfigBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/CommonConfigBuilder.java @@ -4,8 +4,11 @@ import cc.carm.lib.configuration.adapter.ValueType; import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.value.ConfigValue; -public abstract class CommonConfigBuilder, SELF extends CommonConfigBuilder> - extends AbstractConfigBuilder, SELF> { +public abstract class CommonConfigBuilder< + TYPE, UNIT, + RESULT extends ConfigValue, + SELF extends CommonConfigBuilder + > extends AbstractConfigBuilder, SELF> { protected CommonConfigBuilder(ValueType type) { super(ConfigurationHolder.class, type); diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSectionBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/impl/AbstractSectionBuilder.java index b695990..93e6897 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 @@ -4,7 +4,7 @@ import cc.carm.lib.configuration.adapter.ValueAdapter; import cc.carm.lib.configuration.adapter.ValueType; import cc.carm.lib.configuration.builder.CommonConfigBuilder; import cc.carm.lib.configuration.function.DataFunction; -import cc.carm.lib.configuration.function.ValueConsumer; +import cc.carm.lib.configuration.function.ValueComposer; import cc.carm.lib.configuration.function.ValueHandler; import cc.carm.lib.configuration.source.section.ConfigureSection; import cc.carm.lib.configuration.value.ConfigValue; @@ -14,45 +14,45 @@ import java.util.LinkedHashMap; import java.util.Map; public abstract class AbstractSectionBuilder< - TYPE, PARAM, - RESULT extends ConfigValue, - SELF extends AbstractSectionBuilder - > extends CommonConfigBuilder { + TYPE, UNIT, + RESULT extends ConfigValue, + SELF extends AbstractSectionBuilder + > extends CommonConfigBuilder { - protected final @NotNull ValueType paramType; + protected final @NotNull ValueType paramType; - protected @NotNull ValueHandler parser; - protected @NotNull ValueHandler> serializer; + protected @NotNull ValueHandler parser; + protected @NotNull ValueHandler> serializer; - protected AbstractSectionBuilder(@NotNull ValueType type, @NotNull ValueType paramType, - @NotNull ValueHandler parser, - @NotNull ValueHandler> serializer) { + protected AbstractSectionBuilder(@NotNull ValueType type, @NotNull ValueType paramType, + @NotNull ValueHandler parser, + @NotNull ValueHandler> serializer) { super(type); this.paramType = paramType; this.parser = parser; this.serializer = serializer; } - public @NotNull SELF parse(@NotNull DataFunction valueParser) { + public @NotNull SELF parse(@NotNull DataFunction valueParser) { return parse((p, section) -> valueParser.handle(section)); } - public @NotNull SELF parse(@NotNull ValueHandler valueParser) { + public @NotNull SELF parse(@NotNull ValueHandler valueParser) { this.parser = valueParser; return self(); } - public @NotNull SELF serialize(@NotNull ValueHandler> serializer) { + public @NotNull SELF serialize(@NotNull ValueHandler> serializer) { this.serializer = serializer; return self(); } - public @NotNull SELF serialize(@NotNull DataFunction> serializer) { + public @NotNull SELF serialize(@NotNull DataFunction> serializer) { return serialize((p, value) -> serializer.handle(value)); } - public @NotNull SELF serialize(@NotNull ValueConsumer, PARAM> serializer) { + public @NotNull SELF serialize(@NotNull ValueComposer, UNIT> serializer) { return serialize((h, value) -> { Map map = new LinkedHashMap<>(); serializer.accept(h, map, value); @@ -60,7 +60,7 @@ public abstract class AbstractSectionBuilder< }); } - protected ValueAdapter buildAdapter() { + protected ValueAdapter buildAdapter() { return new ValueAdapter<>(this.paramType) .parser((p, type, data) -> { ConfigureSection section = p.deserialize(ConfigureSection.class, data); 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 91d4c48..d7cc247 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 @@ -9,19 +9,19 @@ import cc.carm.lib.configuration.value.ConfigValue; import org.jetbrains.annotations.NotNull; public abstract class AbstractSourceBuilder< - V, SOURCE, PARAM, RESULT extends ConfigValue, - SELF extends AbstractSourceBuilder - > extends CommonConfigBuilder { + V, SOURCE, UNIT, RESULT extends ConfigValue, + SELF extends AbstractSourceBuilder + > extends CommonConfigBuilder { protected final @NotNull ValueType sourceType; - protected final @NotNull ValueType paramType; - protected @NotNull ValueHandler valueParser; - protected @NotNull ValueHandler valueSerializer; + protected final @NotNull ValueType paramType; + protected @NotNull ValueHandler valueParser; + protected @NotNull ValueHandler valueSerializer; protected AbstractSourceBuilder(@NotNull ValueType type, - @NotNull ValueType sourceType, @NotNull ValueType paramType, - @NotNull ValueHandler parser, - @NotNull ValueHandler serializer) { + @NotNull ValueType sourceType, @NotNull ValueType paramType, + @NotNull ValueHandler parser, + @NotNull ValueHandler serializer) { super(type); this.sourceType = sourceType; this.paramType = paramType; @@ -29,25 +29,25 @@ public abstract class AbstractSourceBuilder< this.valueSerializer = serializer; } - public @NotNull SELF parse(@NotNull DataFunction parser) { + public @NotNull SELF parse(@NotNull DataFunction parser) { return parse((p, source) -> parser.handle(source)); } - public @NotNull SELF parse(@NotNull ValueHandler parser) { + public @NotNull SELF parse(@NotNull ValueHandler parser) { this.valueParser = parser; return self(); } - public @NotNull SELF serialize(@NotNull ValueHandler serializer) { + public @NotNull SELF serialize(@NotNull ValueHandler serializer) { this.valueSerializer = serializer; return self(); } - public @NotNull SELF serialize(@NotNull DataFunction serializer) { + public @NotNull SELF serialize(@NotNull DataFunction serializer) { return serialize((p, value) -> serializer.handle(value)); } - protected ValueAdapter buildAdapter() { + protected ValueAdapter buildAdapter() { return new ValueAdapter<>(this.paramType) .parser((holder, type, data) -> { SOURCE source = holder.deserialize(this.sourceType, data); 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 1cd381c..4532552 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 @@ -8,9 +8,11 @@ import cc.carm.lib.configuration.value.standard.ConfiguredList; import org.jetbrains.annotations.NotNull; import java.util.*; +import java.util.function.Consumer; import java.util.function.Supplier; -public class SectionListBuilder extends AbstractSectionBuilder, V, ConfiguredList, SectionListBuilder> { +public class SectionListBuilder + extends AbstractSectionBuilder, V, ConfiguredList, SectionListBuilder> { protected @NotNull Supplier> constructor; @@ -32,6 +34,14 @@ public class SectionListBuilder extends AbstractSectionBuilder, V, Co return defaults(new ArrayList<>(values)); } + public final @NotNull SectionListBuilder defaults(@NotNull Consumer> constructor) { + return defaults(() -> { + List list = new ArrayList<>(); + constructor.accept(list); + return list; + }); + } + public SectionListBuilder constructor(@NotNull Supplier> constructor) { this.constructor = constructor; return this; 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 f750823..09150b4 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 @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.function.Consumer; import java.util.function.Supplier; public class SourceListBuilder @@ -34,6 +35,23 @@ public class SourceListBuilder return defaults(new ArrayList<>(values)); } + public final @NotNull SourceListBuilder defaults(@NotNull Consumer> constructor) { + return defaults(() -> { + List list = new ArrayList<>(); + constructor.accept(list); + return list; + }); + } + + public SourceListBuilder constructor(@NotNull Supplier> constructor) { + this.constructor = constructor; + return this; + } + + public > SourceListBuilder construct(@NotNull LIST list) { + return constructor(() -> list); + } + @Override protected @NotNull SourceListBuilder self() { return this; diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/map/SectionMapBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/map/SectionMapBuilder.java index 9899789..20d8702 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/map/SectionMapBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/map/SectionMapBuilder.java @@ -14,10 +14,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; public class SectionMapBuilder, K, V> - extends AbstractSectionBuilder< - Map, V, ConfiguredMap, - SectionMapBuilder - > { + extends AbstractSectionBuilder, V, ConfiguredMap, SectionMapBuilder> { protected final @NotNull ValueType keyType; diff --git a/core/src/main/java/cc/carm/lib/configuration/builder/map/SourceMapBuilder.java b/core/src/main/java/cc/carm/lib/configuration/builder/map/SourceMapBuilder.java index 5120f46..61de57d 100644 --- a/core/src/main/java/cc/carm/lib/configuration/builder/map/SourceMapBuilder.java +++ b/core/src/main/java/cc/carm/lib/configuration/builder/map/SourceMapBuilder.java @@ -13,10 +13,7 @@ import java.util.function.Consumer; import java.util.function.Supplier; public class SourceMapBuilder, SOURCE, K, V> - extends AbstractSourceBuilder< - Map, SOURCE, V, ConfiguredMap, - SourceMapBuilder - > { + extends AbstractSourceBuilder, SOURCE, V, ConfiguredMap, SourceMapBuilder> { protected final @NotNull ValueType keyType; 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 cc32627..e3753b0 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,6 @@ package cc.carm.lib.configuration.builder.value; import cc.carm.lib.configuration.adapter.ValueType; -import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder; import cc.carm.lib.configuration.function.ValueHandler; import cc.carm.lib.configuration.source.section.ConfigureSection; import org.jetbrains.annotations.NotNull; 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 96b01b0..f14fcf2 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 @@ -6,7 +6,8 @@ import cc.carm.lib.configuration.function.ValueHandler; import cc.carm.lib.configuration.value.standard.ConfiguredValue; import org.jetbrains.annotations.NotNull; -public class SourceValueBuilder extends AbstractSourceBuilder, SourceValueBuilder> { +public class SourceValueBuilder + extends AbstractSourceBuilder, SourceValueBuilder> { public SourceValueBuilder(@NotNull ValueType sourceType, @NotNull ValueType valueType, diff --git a/core/src/main/java/cc/carm/lib/configuration/function/DataValidator.java b/core/src/main/java/cc/carm/lib/configuration/function/DataValidator.java new file mode 100644 index 0000000..e3217fa --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/function/DataValidator.java @@ -0,0 +1,17 @@ +package cc.carm.lib.configuration.function; + +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface DataValidator { + + void validate(@Nullable T value) throws Exception; + + default DataValidator compose(DataValidator other) { + return value -> { + validate(value); + other.validate(value); + }; + } + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/function/ValueComposer.java b/core/src/main/java/cc/carm/lib/configuration/function/ValueComposer.java new file mode 100644 index 0000000..7232de7 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/function/ValueComposer.java @@ -0,0 +1,29 @@ +package cc.carm.lib.configuration.function; + + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface ValueComposer { + + /** + * Accept the value and the data, and then compose the value. + * + * @param holder The configuration holder + * @param type The value type, e.g. {@link java.util.List}, {@link java.util.Map}, etc. + * @param data The unit data + * @throws Exception If an error occurs + */ + void accept(@NotNull ConfigurationHolder holder, @NotNull T type, @NotNull U data) throws Exception; + + default ValueComposer andThen(ValueComposer after) { + return (holder, unit, data) -> { + accept(holder, unit, data); + after.accept(holder, unit, data); + }; + } + +} + + diff --git a/core/src/main/java/cc/carm/lib/configuration/function/ValueConsumer.java b/core/src/main/java/cc/carm/lib/configuration/function/ValueConsumer.java deleted file mode 100644 index fa628c1..0000000 --- a/core/src/main/java/cc/carm/lib/configuration/function/ValueConsumer.java +++ /dev/null @@ -1,21 +0,0 @@ -package cc.carm.lib.configuration.function; - - -import cc.carm.lib.configuration.source.ConfigurationHolder; -import org.jetbrains.annotations.NotNull; - -@FunctionalInterface -public interface ValueConsumer { - - void accept(@NotNull ConfigurationHolder holder, @NotNull U unit, @NotNull T data) throws Exception; - - default ValueConsumer andThen(ValueConsumer after) { - return (holder, unit, data) -> { - accept(holder, unit, data); - after.accept(holder, unit, data); - }; - } - -} - - diff --git a/core/src/main/java/cc/carm/lib/configuration/function/ValueSupplier.java b/core/src/main/java/cc/carm/lib/configuration/function/ValueSupplier.java new file mode 100644 index 0000000..f8a1274 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/function/ValueSupplier.java @@ -0,0 +1,7 @@ +package cc.carm.lib.configuration.function; + +public interface ValueSupplier { + + + +} diff --git a/core/src/main/java/cc/carm/lib/configuration/function/ValueValidator.java b/core/src/main/java/cc/carm/lib/configuration/function/ValueValidator.java new file mode 100644 index 0000000..e99beda --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/function/ValueValidator.java @@ -0,0 +1,47 @@ +package cc.carm.lib.configuration.function; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@FunctionalInterface +public interface ValueValidator { + + void validate(@NotNull ConfigurationHolder holder, @Nullable T value) throws Exception; + + default ValueValidator compose(ValueValidator other) { + return (holder, value) -> { + validate(holder, value); + other.validate(holder, value); + }; + } + + static ValueValidator none() { + return (holder, data) -> { + }; + } + + static ValueValidator nonnull() { + return nonnull("Value cannot be null"); + } + + static ValueValidator nonnull(String message) { + return (holder, data) -> { + if (data == null) throw new IllegalArgumentException(message); + }; + } + + static ValueValidator range(V min, V max) { + return range(min, max, "Value must be in range [" + min + ", " + max + "]"); + } + + static ValueValidator range(V min, V max, String message) { + return (holder, data) -> { + if (data.doubleValue() < min.doubleValue() || data.doubleValue() > max.doubleValue()) { + throw new IllegalArgumentException(message); + } + }; + } + + +} 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 41adb62..ad2aa08 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 @@ -38,10 +38,11 @@ public abstract class ConfigurationFactory< protected @NotNull Map metadata = new HashMap<>(); protected ConfigurationInitializer initializer = new ConfigurationInitializer(); - public ConfigurationFactory() { + protected ConfigurationFactory() { this.adapters.register(StandardAdapters.PRIMITIVES); this.adapters.register(StandardAdapters.SECTIONS); this.adapters.register(StandardAdapters.ENUMS); + this.adapters.register(StandardAdapters.UUID); } protected abstract SELF self(); diff --git a/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationHolder.java b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationHolder.java index 2aa4f56..f06e2af 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationHolder.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/ConfigurationHolder.java @@ -86,7 +86,7 @@ public abstract class ConfigurationHolder> registeredValues() { + public Map> registeredValues() { return extractMetadata(StandardMeta.VALUE); } @@ -129,7 +129,7 @@ public abstract class ConfigurationHolder value) { + public void initialize(@NotNull ValueManifest value) { value.holder(this); } diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigInitializeHandler.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/ConfigInitializeHandler.java index 6823bc7..56e8619 100644 --- 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 @@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable; public interface ConfigInitializeHandler { static ConfigInitializeHandler start() { - return (provider, path, value, instace) -> { + return (provider, path, value, instance) -> { }; } 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 index 69def1b..153727a 100644 --- 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 @@ -21,7 +21,7 @@ import java.util.function.Function; public class ConfigurationInitializer { protected @NotNull PathGenerator pathGenerator; - protected @NotNull ConfigInitializeHandler> valueInitializer; + protected @NotNull ConfigInitializeHandler> valueInitializer; protected @NotNull ConfigInitializeHandler, Object> classInitializer; public ConfigurationInitializer() { @@ -29,7 +29,7 @@ public class ConfigurationInitializer { } public ConfigurationInitializer(@NotNull PathGenerator pathGenerator, - @NotNull ConfigInitializeHandler> valueInitializer, + @NotNull ConfigInitializeHandler> valueInitializer, @NotNull ConfigInitializeHandler, Object> classInitializer) { this.pathGenerator = pathGenerator; this.valueInitializer = valueInitializer; @@ -44,11 +44,11 @@ public class ConfigurationInitializer { return pathGenerator; } - public ConfigInitializeHandler> fieldInitializer() { + public ConfigInitializeHandler> fieldInitializer() { return valueInitializer; } - public void fieldInitializer(@NotNull ConfigInitializeHandler> fieldInitializer) { + public void fieldInitializer(@NotNull ConfigInitializeHandler> fieldInitializer) { this.valueInitializer = fieldInitializer; } @@ -60,7 +60,7 @@ public class ConfigurationInitializer { this.classInitializer = classInitializer; } - public void appendFieldInitializer(@NotNull ConfigInitializeHandler> fieldInitializer) { + public void appendFieldInitializer(@NotNull ConfigInitializeHandler> fieldInitializer) { this.valueInitializer = this.valueInitializer.andThen(fieldInitializer); } @@ -163,9 +163,9 @@ public class ConfigurationInitializer { field.setAccessible(true); Object object = field.get(source); // - if (object instanceof ConfigValue) { + if (object instanceof ConfigValue) { // 目标是 ConfigValue 实例,进行具体的初始化注入 - ConfigValue value = (ConfigValue) object; + ConfigValue value = (ConfigValue) object; String path = getFieldPath(holder, parent, field); if (path == null) return; value.initialize(holder, path); diff --git a/core/src/main/java/cc/carm/lib/configuration/source/meta/StandardMeta.java b/core/src/main/java/cc/carm/lib/configuration/source/meta/StandardMeta.java index 927ee60..547f03f 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/meta/StandardMeta.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/meta/StandardMeta.java @@ -7,6 +7,6 @@ public interface StandardMeta { /** * To mark the {@link ConfigValue} instance of specific path. */ - ConfigurationMetadata> VALUE = ConfigurationMetadata.of(); + ConfigurationMetadata> VALUE = ConfigurationMetadata.of(); } 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 67c5eb3..320183d 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 @@ -33,9 +33,9 @@ import java.util.Optional; * @see ValueManifest Base class providing metadata and default value handling * @see ConfigurationHolder Responsible for configuration source persistence */ -public abstract class ConfigValue extends ValueManifest { +public abstract class ConfigValue extends ValueManifest { - protected ConfigValue(@NotNull ValueManifest manifest) { + protected ConfigValue(@NotNull ValueManifest manifest) { super(manifest); } 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 58245e7..e81bd76 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 @@ -1,6 +1,7 @@ package cc.carm.lib.configuration.value; import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.function.ValueValidator; import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder; import cc.carm.lib.configuration.source.section.ConfigureSource; @@ -11,37 +12,48 @@ import org.jetbrains.annotations.Nullable; import java.util.function.BiConsumer; import java.util.function.Supplier; -public class ValueManifest { +public class ValueManifest { - protected final @NotNull ValueType type; + protected final @NotNull ValueType type; protected final @NotNull BiConsumer<@NotNull ConfigurationHolder, @NotNull String> initializer; protected @Nullable ConfigurationHolder holder; protected @Nullable String path; // Section path - protected @NotNull Supplier<@Nullable T> defaultSupplier; + protected @NotNull ValueValidator validator; + protected @NotNull Supplier<@Nullable TYPE> defaultSupplier; - public ValueManifest(@NotNull ValueType type) { - this(type, () -> null, EMPTY_INITIALIZER, null, null); + public ValueManifest(@NotNull ValueType type) { + this(type, () -> null, ValueValidator.none(), EMPTY_INITIALIZER, null, null); } - public ValueManifest(@NotNull T defaultValue) { + public ValueManifest(@NotNull TYPE defaultValue) { this(ValueType.of(defaultValue), () -> defaultValue); } - public ValueManifest(@NotNull ValueType type, @NotNull Supplier<@Nullable T> defaultSupplier) { - this(type, defaultSupplier, EMPTY_INITIALIZER, null, null); + public ValueManifest(@NotNull ValueType type, @NotNull Supplier<@Nullable TYPE> defaultSupplier) { + this(type, defaultSupplier, ValueValidator.none(), EMPTY_INITIALIZER, null, null); } - public ValueManifest(@NotNull ValueType type, @NotNull Supplier<@Nullable T> defaultSupplier, + public ValueManifest(@NotNull ValueType type, + @NotNull Supplier<@Nullable TYPE> defaultSupplier, + @NotNull ValueValidator validator) { + this(type, defaultSupplier, validator, EMPTY_INITIALIZER, null, null); + } + + public ValueManifest(@NotNull ValueType type, @NotNull Supplier<@Nullable TYPE> defaultSupplier, + @NotNull ValueValidator validator, @NotNull BiConsumer<@NotNull ConfigurationHolder, @NotNull String> initializer) { - this(type, defaultSupplier, initializer, null, null); + this(type, defaultSupplier, validator, initializer, null, null); } - public ValueManifest(@NotNull ValueType type, @NotNull Supplier<@Nullable T> defaultSupplier, + public ValueManifest(@NotNull ValueType type, + @NotNull Supplier<@Nullable TYPE> defaultSupplier, + @NotNull ValueValidator validator, @NotNull BiConsumer<@NotNull ConfigurationHolder, @NotNull String> initializer, @Nullable ConfigurationHolder holder, @Nullable String path) { this.type = type; + this.validator = validator; this.initializer = initializer; this.defaultSupplier = defaultSupplier; this.holder = holder; @@ -49,8 +61,8 @@ public class ValueManifest { initialize(); } - protected ValueManifest(@NotNull ValueManifest manifest) { - this(manifest.type, manifest.defaultSupplier, manifest.initializer, manifest.holder, manifest.path); + protected ValueManifest(@NotNull ValueManifest manifest) { + this(manifest.type, manifest.defaultSupplier, manifest.validator, manifest.initializer, manifest.holder, manifest.path); } public void initialize(@NotNull ConfigurationHolder holder, @NotNull String path) { @@ -63,7 +75,7 @@ public class ValueManifest { if (holder != null && path != null) this.initializer.accept(holder, path); } - public @NotNull ValueType type() { + public @NotNull ValueType type() { return this.type; } @@ -75,20 +87,40 @@ public class ValueManifest { this.path = path; } - public @Nullable T defaults() { + public @Nullable TYPE defaults() { return this.defaultSupplier.get(); } - public void defaults(@Nullable T defaultValue) { + public void defaults(@Nullable TYPE defaultValue) { defaults(() -> defaultValue); } - public void defaults(@NotNull Supplier<@Nullable T> defaultValue) { + public void defaults(@NotNull Supplier<@Nullable TYPE> defaultValue) { this.defaultSupplier = defaultValue; } public boolean hasDefaults() { - return this.defaultSupplier.get() != null; + return defaults() != null; + } + + public @NotNull ValueValidator validator() { + return this.validator; + } + + public void validator(@NotNull ValueValidator validator) { + this.validator = validator; + } + + public void validate(@NotNull ValueValidator validator) { + validator((h, v) -> { + this.validator.validate(h, v); + validator.validate(h, v); + }); + } + + protected UNIT withValidated(@Nullable UNIT value) throws Exception { + validator.validate(holder(), value); + return value; } public @NotNull String path() { @@ -105,7 +137,7 @@ public class ValueManifest { return holder().config(); } - public ConfigurationMetaHolder metadata() { + public @NotNull ConfigurationMetaHolder metadata() { return holder().metadata(path()); } diff --git a/core/src/main/java/cc/carm/lib/configuration/value/impl/CachedConfigValue.java b/core/src/main/java/cc/carm/lib/configuration/value/impl/CachedConfigValue.java index 6d18db5..e6a963a 100644 --- a/core/src/main/java/cc/carm/lib/configuration/value/impl/CachedConfigValue.java +++ b/core/src/main/java/cc/carm/lib/configuration/value/impl/CachedConfigValue.java @@ -9,12 +9,12 @@ import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public abstract class CachedConfigValue extends ConfigValue { +public abstract class CachedConfigValue extends ConfigValue { protected @Nullable T cachedValue; protected long parsedTime = -1; - protected CachedConfigValue(@NotNull ValueManifest manifest) { + protected CachedConfigValue(@NotNull ValueManifest manifest) { super(manifest); } diff --git a/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredList.java b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredList.java index 26c1f43..969cfc9 100644 --- a/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredList.java +++ b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredList.java @@ -5,6 +5,7 @@ import cc.carm.lib.configuration.adapter.ValueParser; import cc.carm.lib.configuration.adapter.ValueSerializer; import cc.carm.lib.configuration.adapter.ValueType; import cc.carm.lib.configuration.builder.list.ConfigListBuilder; +import cc.carm.lib.configuration.builder.list.SourceListBuilder; import cc.carm.lib.configuration.value.ValueManifest; import cc.carm.lib.configuration.value.impl.CachedConfigValue; import org.jetbrains.annotations.NotNull; @@ -15,7 +16,7 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -public class ConfiguredList extends CachedConfigValue> implements List { +public class ConfiguredList extends CachedConfigValue, V> implements List { public static @NotNull ConfigListBuilder builderOf(@NotNull Class type) { return builderOf(ValueType.of(type)); @@ -25,10 +26,26 @@ public class ConfiguredList extends CachedConfigValue> implements Lis return new ConfigListBuilder<>(type); } + public static @NotNull SourceListBuilder with(@NotNull Class registeredType) { + return with(ValueType.of(registeredType)); + } + + public static @NotNull SourceListBuilder with(@NotNull ValueType registeredType) { + return new ConfigListBuilder<>(registeredType).from(registeredType); + } + + @SafeVarargs + public static @NotNull ConfiguredList of(@NotNull T value, @NotNull T... values) { + List list = new ArrayList<>(); + list.add(value); + Collections.addAll(list, values); + return with(ValueType.of(value)).defaults(list).build(); + } + protected final @NotNull Supplier> constructor; protected final @NotNull ValueAdapter paramAdapter; - public ConfiguredList(@NotNull ValueManifest> manifest, + public ConfiguredList(@NotNull ValueManifest, V> manifest, @NotNull Supplier> constructor, @NotNull ValueAdapter paramAdapter) { super(manifest); @@ -79,7 +96,7 @@ public class ConfiguredList extends CachedConfigValue> implements Lis for (Object dataVal : data) { if (dataVal == null) continue; try { - list.add(parser.parse(holder(), paramType(), dataVal)); + list.add(withValidated(parser.parse(holder(), paramType(), dataVal))); } catch (Exception e) { e.printStackTrace(); } @@ -88,20 +105,21 @@ public class ConfiguredList extends CachedConfigValue> implements Lis } @Override - public void set(@Nullable List value) { - updateCache(value); - if (value == null) { + public void set(@Nullable List list) { + updateCache(list); + if (list == null) { setData(null); return; } - + ValueSerializer serializer = serializer(); if (serializer == null) return; + List data = new ArrayList<>(); - for (V val : value) { + for (V val : list) { if (val == null) continue; try { - data.add(serializer.serialize(holder(), paramType(), val)); + data.add(serializer.serialize(holder(), paramType(), withValidated(val))); } catch (Exception ex) { ex.printStackTrace(); } diff --git a/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredMap.java b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredMap.java index 6784eb5..601d635 100644 --- a/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredMap.java +++ b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredMap.java @@ -17,7 +17,11 @@ import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; -public class ConfiguredMap extends CachedConfigValue> implements Map { +public class ConfiguredMap extends CachedConfigValue, V> implements Map { + + public static ConfigMapCreator builderOf(@NotNull Class valueType) { + return builderOf(String.class, valueType); + } public static ConfigMapCreator builderOf(@NotNull ValueType keyType, @NotNull ValueType valueType) { return new ConfigMapCreator<>(keyType, valueType); @@ -38,7 +42,7 @@ public class ConfiguredMap extends CachedConfigValue> implements protected final @NotNull ValueAdapter keyAdapter; protected final @NotNull ValueAdapter valueAdapter; - public ConfiguredMap(@NotNull ValueManifest> manifest, + public ConfiguredMap(@NotNull ValueManifest, V> manifest, @NotNull Supplier> constructor, @NotNull ValueAdapter keyAdapter, @NotNull ValueAdapter valueAdapter) { super(manifest); @@ -80,8 +84,9 @@ public class ConfiguredMap extends CachedConfigValue> implements if (keys.isEmpty()) return getDefaultFirst(map); ValueParser keyParser = parserFor(keyAdapter); + if (keyParser == null) return getDefaultFirst(map); ValueParser valueParser = parserFor(valueAdapter); - if (keyParser == null || valueParser == null) return getDefaultFirst(map); + if (valueParser == null) return getDefaultFirst(map); for (String dataKey : keys) { Object dataVal = section.get(dataKey); @@ -89,7 +94,7 @@ public class ConfiguredMap extends CachedConfigValue> implements try { K key = keyParser.parse(holder(), keyType(), dataKey); V value = valueParser.parse(holder(), valueType(), dataVal); - map.put(key, value); + map.put(key, withValidated(value)); } catch (Exception e) { e.printStackTrace(); } @@ -116,8 +121,9 @@ public class ConfiguredMap extends CachedConfigValue> implements } ValueSerializer keySerializer = serializerFor(keyAdapter); + if (keySerializer == null) return; ValueSerializer valueSerializer = serializerFor(valueAdapter); - if (keySerializer == null || valueSerializer == null) return; + if (valueSerializer == null) return; Map data = new LinkedHashMap<>(); @@ -125,7 +131,7 @@ public class ConfiguredMap extends CachedConfigValue> implements try { data.put( keySerializer.serialize(holder(), keyType(), entry.getKey()), - valueSerializer.serialize(holder(), valueType(), entry.getValue()) + valueSerializer.serialize(holder(), valueType(), withValidated(entry.getValue())) ); } catch (Exception e) { e.printStackTrace(); diff --git a/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredValue.java b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredValue.java index 770d76b..4c41751 100644 --- a/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredValue.java +++ b/core/src/main/java/cc/carm/lib/configuration/value/standard/ConfiguredValue.java @@ -5,6 +5,8 @@ import cc.carm.lib.configuration.adapter.ValueParser; import cc.carm.lib.configuration.adapter.ValueSerializer; import cc.carm.lib.configuration.adapter.ValueType; import cc.carm.lib.configuration.builder.value.ConfigValueBuilder; +import cc.carm.lib.configuration.builder.value.SourceValueBuilder; +import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.value.ValueManifest; import cc.carm.lib.configuration.value.impl.CachedConfigValue; import org.jetbrains.annotations.NotNull; @@ -12,16 +14,54 @@ import org.jetbrains.annotations.Nullable; import java.util.function.Supplier; -public class ConfiguredValue extends CachedConfigValue { +public class ConfiguredValue extends CachedConfigValue { + /** + * Create a new value builder. + * + * @param type The type of the value. + * @param The type of the value. + * @return a {@link ConfigValueBuilder} with the specified type. + */ public static ConfigValueBuilder builderOf(@NotNull Class type) { return new ConfigValueBuilder<>(ValueType.of(type)); } + /** + * Create a new value builder. + * + * @param type The type of the value. + * @param The type of the value. + * @return a {@link ConfigValueBuilder} with the specified type. + */ public static ConfigValueBuilder builderOf(@NotNull ValueType type) { return new ConfigValueBuilder<>(type); } + /** + * Create a new value builder with the specified {@link ConfigurationHolder#registeredValues()} type. + * + * @param registeredType The type of the value. + * @param The type of the value. + * @return a {@link SourceValueBuilder} with the specified registered type. + * @see ValueAdapter + */ + public static SourceValueBuilder with(@NotNull Class registeredType) { + return with(ValueType.of(registeredType)); + } + + /** + * Create a new value builder with the specified {@link ConfigurationHolder#registeredValues()} type. + * + * @param registeredType The type of the value. + * @param The type of the value. + * @return a {@link SourceValueBuilder} with the specified registered type. + * @see ValueAdapter + */ + public static SourceValueBuilder with(@NotNull ValueType registeredType) { + return new ConfigValueBuilder<>(registeredType).from(registeredType); + } + public static ConfiguredValue of(@NotNull V defaults) { return of(ValueType.of(defaults), () -> defaults); } @@ -51,7 +91,7 @@ public class ConfiguredValue extends CachedConfigValue { ); } - public static ConfiguredValue of(@NotNull ValueManifest manifest, + public static ConfiguredValue of(@NotNull ValueManifest manifest, @Nullable ValueParser parser, @Nullable ValueSerializer serializer) { ValueAdapter adapter = new ValueAdapter<>(manifest.type()); @@ -60,13 +100,13 @@ public class ConfiguredValue extends CachedConfigValue { return of(manifest, adapter); } - public static ConfiguredValue of(@NotNull ValueManifest manifest, @NotNull ValueAdapter adapter) { + public static ConfiguredValue of(@NotNull ValueManifest manifest, @NotNull ValueAdapter adapter) { return new ConfiguredValue<>(manifest, adapter); } protected final @NotNull ValueAdapter adapter; - public ConfiguredValue(@NotNull ValueManifest manifest, @NotNull ValueAdapter adapter) { + public ConfiguredValue(@NotNull ValueManifest manifest, @NotNull ValueAdapter adapter) { super(manifest); this.adapter = adapter; } @@ -103,11 +143,13 @@ public class ConfiguredValue extends CachedConfigValue { ValueParser parser = parser(); if (parser == null) return defaults(); // No parser, return default value. + try { // If there are no errors, update the cache and return. - return updateCache(parser.parse(holder(), type(), data)); + V parsed = parser.parse(holder(), type(), data); + return updateCache(withValidated(parsed)); } catch (Exception e) { - // There was a parsing error, prompted and returned the default value. + // There was a validate or parsing error, prompted and returned the default value. e.printStackTrace(); return defaults(); } @@ -121,18 +163,18 @@ public class ConfiguredValue extends CachedConfigValue { * @param value The value to be set */ @Override - public void set(V value) { + public void set(@Nullable V value) { updateCache(value); // Update cache if (value == null) { setData(null); return; } - + ValueSerializer serializer = serializer(); if (serializer == null) return; // No serializer, do nothing. try { - setData(serializer.serialize(holder(), type(), value)); + setData(serializer.serialize(holder(), type(), withValidated(value))); } catch (Exception e) { e.printStackTrace(); } diff --git a/demo/pom.xml b/demo/pom.xml index f9c3224..e2ce12c 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -5,7 +5,7 @@ configured-parent cc.carm.lib - 4.1.0 + 4.1.1 4.0.0 diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java index e9a2427..50c27e9 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java @@ -4,7 +4,6 @@ import cc.carm.lib.configuration.Configuration; import cc.carm.lib.configuration.annotation.*; import cc.carm.lib.configuration.demo.tests.model.ItemStack; import cc.carm.lib.configuration.demo.tests.model.UserRecord; -import cc.carm.lib.configuration.value.ConfigValue; import cc.carm.lib.configuration.value.standard.ConfiguredList; import cc.carm.lib.configuration.value.standard.ConfiguredMap; import cc.carm.lib.configuration.value.standard.ConfiguredValue; @@ -26,15 +25,18 @@ public interface DemoConfiguration extends Configuration { @ConfigPath(root = true) @ConfigVersion(2) - ConfigValue VERSION = ConfiguredValue.of(Double.class, 2.0D); + ConfiguredValue VERSION = ConfiguredValue.of(Double.class, 2.0D); @ConfigPath(root = true) @FooterComments({"此处内容将显示在配置条目的下方", "可用于补充说明,但一般不建议使用"}) - ConfigValue TEST_NUMBER = ConfiguredValue.of(1000000L); + ConfiguredValue TEST_NUMBER = ConfiguredValue.with(Long.class) + .validate(l -> l > 100, "数值必须大于100") + .validate(l -> l < 100000000, "数值必须小于100000000") + .defaults(123456789L).build(); @HeaderComments({"枚举类型测试"}) @FooterComments({"上述的枚举内容本质上是通过STRING解析的"}) - ConfigValue TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS); + ConfiguredValue TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS); @HeaderComments({"空值测试"}) @InlineComment("空值Inline注释") @@ -72,7 +74,7 @@ public interface DemoConfiguration extends Configuration { class SUB implements Configuration { @ConfigPath(value = "uuid-value", root = true) - public static final ConfigValue UUID_CONFIG_VALUE = ConfiguredValue + public static final ConfiguredValue UUID_CONFIG_VALUE = ConfiguredValue .builderOf(UUID.class).fromString() .parse((holder, data) -> UUID.fromString(data)) .build(); diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/InstanceConfig.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/InstanceConfig.java index 4d69316..ea68417 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/InstanceConfig.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/InstanceConfig.java @@ -2,12 +2,11 @@ package cc.carm.lib.configuration.demo.tests.conf; import cc.carm.lib.configuration.Configuration; import cc.carm.lib.configuration.annotation.HeaderComments; -import cc.carm.lib.configuration.value.ConfigValue; import cc.carm.lib.configuration.value.standard.ConfiguredValue; @HeaderComments("Inner Test") public class InstanceConfig implements Configuration { - public final ConfigValue STATUS = ConfiguredValue.of(1.0D); + public final ConfiguredValue STATUS = ConfiguredValue.of(1.0D); } diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/RegistryConfig.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/RegistryConfig.java index 7d5d922..e78ea15 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/RegistryConfig.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/RegistryConfig.java @@ -6,7 +6,6 @@ import cc.carm.lib.configuration.annotation.FooterComments; import cc.carm.lib.configuration.annotation.HeaderComments; import cc.carm.lib.configuration.annotation.InlineComment; import cc.carm.lib.configuration.demo.tests.model.UserRecord; -import cc.carm.lib.configuration.value.ConfigValue; import cc.carm.lib.configuration.value.standard.ConfiguredValue; import java.util.UUID; @@ -20,7 +19,7 @@ public class RegistryConfig implements Configuration { @FooterComments({"12313213212"}) @InlineComment(value = "用户名(匹配注释)", regex = "name") // 通过注解给配置添加注释。 @InlineComment(value = "信息", regex = {"info.*", "info.game.*"}) // 通过注解给配置添加注释。 - public final ConfigValue OWNER = ConfiguredValue.builderOf(UserRecord.class).fromSection() + public final ConfiguredValue OWNER = ConfiguredValue.builderOf(UserRecord.class).fromSection() .defaults(new UserRecord("Carm", UUID.randomUUID())) .parse((holder, section) -> UserRecord.deserialize(section)) .serialize((holder, data) -> data.serialize()).build(); diff --git a/features/commentable/pom.xml b/features/commentable/pom.xml index 4c44c9c..aa4eb2e 100644 --- a/features/commentable/pom.xml +++ b/features/commentable/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.1.0 + 4.1.1 ../../pom.xml diff --git a/features/file/pom.xml b/features/file/pom.xml index d18ba74..b6b27ee 100644 --- a/features/file/pom.xml +++ b/features/file/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.1.0 + 4.1.1 ../../pom.xml diff --git a/features/section/pom.xml b/features/section/pom.xml index 446a617..6376f05 100644 --- a/features/section/pom.xml +++ b/features/section/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.1.0 + 4.1.1 ../../pom.xml diff --git a/features/text/pom.xml b/features/text/pom.xml index 425a1cb..f489317 100644 --- a/features/text/pom.xml +++ b/features/text/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.1.0 + 4.1.1 ../../pom.xml diff --git a/features/validators/pom.xml b/features/validators/pom.xml new file mode 100644 index 0000000..8a9aa77 --- /dev/null +++ b/features/validators/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + cc.carm.lib + configured-parent + 4.1.1 + ../../pom.xml + + + ${project.jdk.version} + ${project.jdk.version} + UTF-8 + UTF-8 + + + configured-feature-validators + jar + + + + ${project.groupId} + configured-core + ${project.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + \ No newline at end of file diff --git a/features/validators/src/main/java/cc/carm/lib/configuration/annotation/ValuePattern.java b/features/validators/src/main/java/cc/carm/lib/configuration/annotation/ValuePattern.java new file mode 100644 index 0000000..aa5e6f1 --- /dev/null +++ b/features/validators/src/main/java/cc/carm/lib/configuration/annotation/ValuePattern.java @@ -0,0 +1,16 @@ +package cc.carm.lib.configuration.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValuePattern { + + String value(); + + String message() default "Invalid value format!"; + +} diff --git a/features/validators/src/main/java/cc/carm/lib/configuration/annotation/ValueRange.java b/features/validators/src/main/java/cc/carm/lib/configuration/annotation/ValueRange.java new file mode 100644 index 0000000..8f09ad7 --- /dev/null +++ b/features/validators/src/main/java/cc/carm/lib/configuration/annotation/ValueRange.java @@ -0,0 +1,18 @@ +package cc.carm.lib.configuration.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ValueRange { + + long min() default Long.MIN_VALUE; + + long max() default Long.MAX_VALUE; + + String message() default "Value out of range."; + +} diff --git a/features/validators/src/main/java/cc/carm/lib/configuration/validators/StandardValidators.java b/features/validators/src/main/java/cc/carm/lib/configuration/validators/StandardValidators.java new file mode 100644 index 0000000..cb029b0 --- /dev/null +++ b/features/validators/src/main/java/cc/carm/lib/configuration/validators/StandardValidators.java @@ -0,0 +1,8 @@ +package cc.carm.lib.configuration.validators; + +public class StandardValidators { + + + + +} diff --git a/features/versioned/pom.xml b/features/versioned/pom.xml index 8015fb4..6144283 100644 --- a/features/versioned/pom.xml +++ b/features/versioned/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.1.0 + 4.1.1 ../../pom.xml diff --git a/pom.xml b/pom.xml index 48d8334..0b82af7 100644 --- a/pom.xml +++ b/pom.xml @@ -15,13 +15,14 @@ cc.carm.lib configured-parent pom - 4.1.0 + 4.1.1 core features/section features/file features/commentable features/versioned + features/validators features/text providers/yaml diff --git a/providers/gson/pom.xml b/providers/gson/pom.xml index 94ed438..d4d183c 100644 --- a/providers/gson/pom.xml +++ b/providers/gson/pom.xml @@ -5,7 +5,7 @@ configured-parent cc.carm.lib - 4.1.0 + 4.1.1 ../../pom.xml 4.0.0 diff --git a/providers/hocon/pom.xml b/providers/hocon/pom.xml index 1c7bd37..bcb783e 100644 --- a/providers/hocon/pom.xml +++ b/providers/hocon/pom.xml @@ -6,7 +6,7 @@ cc.carm.lib configured-parent - 4.1.0 + 4.1.1 ../../pom.xml diff --git a/providers/mongodb/pom.xml b/providers/mongodb/pom.xml index b7a8f1b..0cfae96 100644 --- a/providers/mongodb/pom.xml +++ b/providers/mongodb/pom.xml @@ -5,7 +5,7 @@ configured-parent cc.carm.lib - 4.1.0 + 4.1.1 ../../pom.xml 4.0.0 diff --git a/providers/sql/pom.xml b/providers/sql/pom.xml index 3c1786c..5a4b266 100644 --- a/providers/sql/pom.xml +++ b/providers/sql/pom.xml @@ -6,7 +6,7 @@ configured-parent cc.carm.lib - 4.1.0 + 4.1.1 ../../pom.xml diff --git a/providers/yaml/pom.xml b/providers/yaml/pom.xml index f381860..d754cbb 100644 --- a/providers/yaml/pom.xml +++ b/providers/yaml/pom.xml @@ -6,7 +6,7 @@ configured-parent cc.carm.lib - 4.1.0 + 4.1.1 ../../pom.xml