From db8b227317fc282cfdb044e03fb46d7f54b6b3ae Mon Sep 17 00:00:00 2001 From: carm Date: Sat, 23 Dec 2023 22:24:34 +0800 Subject: [PATCH] feat(interface): Support interface root configurations. --- .../configuration/core/ConfigInitializer.java | 30 +++++++++---------- .../lib/configuration/core/Configuration.java | 8 +++++ .../configuration/core/ConfigurationRoot.java | 5 ++-- .../core/source/ConfigurationProvider.java | 27 +++++++++-------- .../core/value/type/ConfiguredList.java | 3 +- .../core/value/type/ConfiguredSection.java | 24 +++++++++++++-- .../core/value/type/ConfiguredValue.java | 30 +++++++++++++++++-- .../demo/tests/conf/DemoConfiguration.java | 19 ++++++------ .../test/easyconfiguration/HOCONTest.java | 12 +++++--- .../config/{Config.java => SimpleConfig.java} | 17 ++++++----- 10 files changed, 116 insertions(+), 59 deletions(-) create mode 100644 core/src/main/java/cc/carm/lib/configuration/core/Configuration.java rename impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/config/{Config.java => SimpleConfig.java} (53%) diff --git a/core/src/main/java/cc/carm/lib/configuration/core/ConfigInitializer.java b/core/src/main/java/cc/carm/lib/configuration/core/ConfigInitializer.java index 623746a..1ea3f22 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/ConfigInitializer.java +++ b/core/src/main/java/cc/carm/lib/configuration/core/ConfigInitializer.java @@ -14,7 +14,7 @@ import java.util.List; /** * 配置文件类初始化方法 - * 用于初始化 {@link ConfigurationRoot} 中的每个 {@link ConfigValue} 对象 + * 用于初始化 {@link Configuration} 中的每个 {@link ConfigValue} 对象 * * @param {@link ConfigurationProvider} 配置文件的数据来源 * @author CarmJos @@ -30,21 +30,21 @@ public class ConfigInitializer> { /** * 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。 * - * @param clazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 + * @param clazz 配置文件类,须继承于 {@link Configuration} 。 * @param saveDefaults 是否写入默认值(默认为 true)。 */ - public void initialize(@NotNull Class clazz, boolean saveDefaults) { + public void initialize(@NotNull Class clazz, boolean saveDefaults) { initialize(clazz, saveDefaults, true); } /** * 初始化指定类的所有 {@link ConfigValue} 对象。 * - * @param clazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 + * @param clazz 配置文件类,须继承于 {@link Configuration} 。 * @param saveDefaults 是否写入默认值(默认为 true)。 * @param loadSubClasses 是否加载内部子类(默认为 true)。 */ - public void initialize(@NotNull Class clazz, + public void initialize(@NotNull Class clazz, boolean saveDefaults, boolean loadSubClasses) { initializeStaticClass( clazz, null, null, @@ -61,21 +61,21 @@ public class ConfigInitializer> { } /** - * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。 + * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。 * - * @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。 + * @param config 配置文件实例类,须实现 {@link Configuration} 。 */ - public void initialize(@NotNull ConfigurationRoot config) { + public void initialize(@NotNull Configuration config) { initialize(config, true); } /** - * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。 + * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。 * - * @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。 + * @param config 配置文件实例类,须实现 {@link Configuration} 。 * @param saveDefaults 是否写入默认值(默认为 true)。 */ - public void initialize(@NotNull ConfigurationRoot config, boolean saveDefaults) { + public void initialize(@NotNull Configuration config, boolean saveDefaults) { initializeInstance( config, null, null, null, null, null, @@ -92,7 +92,7 @@ public class ConfigInitializer> { // 针对实例类的初始化方法 - private void initializeInstance(@NotNull ConfigurationRoot root, + private void initializeInstance(@NotNull Configuration root, @Nullable String parentPath, @Nullable String fieldName, @Nullable ConfigPath fieldPath, @Nullable HeaderComment fieldHeaderComments, @@ -114,7 +114,7 @@ public class ConfigInitializer> { @Nullable HeaderComment fieldHeaderComments, @Nullable InlineComment fieldInlineComments, boolean saveDefaults, boolean loadSubClasses) { - if (!ConfigurationRoot.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类 + if (!Configuration.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类 String path = getClassPath(clazz, parentPath, fieldName, fieldPath); this.provider.setHeaderComment(path, getClassHeaderComments(clazz, fieldHeaderComments)); if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments)); @@ -147,10 +147,10 @@ public class ConfigInitializer> { field.getAnnotation(InlineComment.class), saveDefaults ); - } else if (source instanceof ConfigurationRoot && object instanceof ConfigurationRoot) { + } else if (source instanceof Configuration && object instanceof Configuration) { // 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。 initializeInstance( - (ConfigurationRoot) object, parent, field.getName(), + (Configuration) object, parent, field.getName(), field.getAnnotation(ConfigPath.class), field.getAnnotation(HeaderComment.class), field.getAnnotation(InlineComment.class), diff --git a/core/src/main/java/cc/carm/lib/configuration/core/Configuration.java b/core/src/main/java/cc/carm/lib/configuration/core/Configuration.java new file mode 100644 index 0000000..1ba41b9 --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/core/Configuration.java @@ -0,0 +1,8 @@ +package cc.carm.lib.configuration.core; + +/** + * The root interface of the configuration file interfaces, + * which is used to label and record the configuration information. + */ +public interface Configuration { +} diff --git a/core/src/main/java/cc/carm/lib/configuration/core/ConfigurationRoot.java b/core/src/main/java/cc/carm/lib/configuration/core/ConfigurationRoot.java index 687e067..8197290 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/ConfigurationRoot.java +++ b/core/src/main/java/cc/carm/lib/configuration/core/ConfigurationRoot.java @@ -1,7 +1,8 @@ package cc.carm.lib.configuration.core; /** - * 配置文件类的根节点,用于标注该类用于记录配置文件中的配置信息。 + * The root node of the configuration file class, + * which is used to label and record the configuration information. */ -public abstract class ConfigurationRoot { +public abstract class ConfigurationRoot implements Configuration { } diff --git a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationProvider.java b/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationProvider.java index 6036e36..b628326 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationProvider.java +++ b/core/src/main/java/cc/carm/lib/configuration/core/source/ConfigurationProvider.java @@ -1,7 +1,8 @@ package cc.carm.lib.configuration.core.source; import cc.carm.lib.configuration.core.ConfigInitializer; -import cc.carm.lib.configuration.core.ConfigurationRoot; +import cc.carm.lib.configuration.core.Configuration; +import cc.carm.lib.configuration.core.Configuration; import cc.carm.lib.configuration.core.value.ConfigValue; import cc.carm.lib.configuration.core.value.impl.CachedConfigValue; import org.jetbrains.annotations.NotNull; @@ -101,49 +102,49 @@ public abstract class ConfigurationProvider> { /** * 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。 * - * @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 + * @param configClazz 配置文件类,须继承于 {@link Configuration} 。 */ - public void initialize(Class configClazz) { + public void initialize(Class configClazz) { initialize(configClazz, true); } /** * 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。 * - * @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 + * @param configClazz 配置文件类,须继承于 {@link Configuration} 。 * @param saveDefaults 是否写入默认值(默认为 true)。 */ - public void initialize(Class configClazz, boolean saveDefaults) { + public void initialize(Class configClazz, boolean saveDefaults) { this.getInitializer().initialize(configClazz, saveDefaults); } /** * 初始化指定类的所有 {@link ConfigValue} 对象。 * - * @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 + * @param configClazz 配置文件类,须继承于 {@link Configuration} 。 * @param saveDefaults 是否写入默认值(默认为 true)。 * @param loadSubClasses 是否加载内部子类(默认为 true)。 */ - public void initialize(Class configClazz, boolean saveDefaults, boolean loadSubClasses) { + public void initialize(Class configClazz, boolean saveDefaults, boolean loadSubClasses) { this.getInitializer().initialize(configClazz, saveDefaults, loadSubClasses); } /** - * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。 + * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。 * - * @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。 + * @param config 配置文件实例类,须实现 {@link Configuration} 。 */ - public void initialize(@NotNull ConfigurationRoot config) { + public void initialize(@NotNull Configuration config) { this.getInitializer().initialize(config, true); } /** - * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。 + * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。 * - * @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。 + * @param config 配置文件实例类,须实现 {@link Configuration} 。 * @param saveDefaults 是否写入默认值(默认为 true)。 */ - public void initialize(@NotNull ConfigurationRoot config, boolean saveDefaults) { + public void initialize(@NotNull Configuration config, boolean saveDefaults) { this.getInitializer().initialize(config, saveDefaults); } diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredList.java b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredList.java index 5bd0cea..beb210c 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredList.java +++ b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredList.java @@ -43,8 +43,7 @@ public class ConfiguredList extends CachedConfigValue> implements Lis @Override public @NotNull List get() { if (!isExpired()) return getCachedOrDefault(new ArrayList<>()); - - // 已过时的数据,需要重新解析一次。 + // Data that is outdated and needs to be parsed again. List list = new ArrayList<>(); List data = getConfiguration().contains(getConfigPath()) ? getConfiguration().getList(getConfigPath()) : null; diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSection.java b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSection.java index 171dd28..76d807e 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSection.java +++ b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredSection.java @@ -31,37 +31,55 @@ public class ConfiguredSection extends CachedConfigValue { this.serializer = serializer; } + /** + * @return Value's type class + */ public @NotNull Class getValueClass() { return valueClass; } + /** + * @return Value's parser, cast value from section. + */ public @NotNull ConfigValueParser, V> getParser() { return parser; } + /** + * @return Value's serializer, serialize value to section. + */ public @NotNull ConfigDataFunction> getSerializer() { return serializer; } + /** + * @return Get the value that parsed from the configuration section. + */ @Override public @Nullable V get() { if (!isExpired()) return getCachedOrDefault(); - // 已过时的数据,需要重新解析一次。 + // Data that is outdated and needs to be parsed again. ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath()); if (section == null) return getDefaultValue(); try { - // 若未出现错误,则直接更新缓存并返回。 + // If there are no errors, update the cache and return. return updateCache(this.parser.parse(section, this.defaultValue)); } catch (Exception e) { - // 出现了解析错误,提示并返回默认值。 + // There was a parsing error, prompted and returned the default value. e.printStackTrace(); return getDefaultValue(); } } + /** + * Use the specified value to update the configuration section. + * Will use {@link #getSerializer()} to serialize the value to section. + * + * @param value The value that needs to be set in the configuration. + */ @Override public void set(V value) { updateCache(value); diff --git a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredValue.java b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredValue.java index d48095f..6a95b39 100644 --- a/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredValue.java +++ b/core/src/main/java/cc/carm/lib/configuration/core/value/type/ConfiguredValue.java @@ -14,6 +14,11 @@ public class ConfiguredValue extends CachedConfigValue { return builder().asValue(valueClass); } + @SuppressWarnings("unchecked") + public static ConfiguredValue of(@NotNull V defaultValue) { + return of((Class) defaultValue.getClass(), defaultValue); + } + public static ConfiguredValue of(Class valueClass) { return of(valueClass, null); } @@ -36,32 +41,51 @@ public class ConfiguredValue extends CachedConfigValue { this.serializer = serializer; } + /** + * @return Value's type class + */ public @NotNull Class getValueClass() { return valueClass; } + /** + * @return Value's parser, cast value from base object. + */ public @NotNull ConfigValueParser getParser() { return parser; } + /** + * @return Value's serializer, serialize value to base object. + */ + public @NotNull ConfigDataFunction getSerializer() { + return serializer; + } + @Override public V get() { if (!isExpired()) return getCachedOrDefault(); - // 已过时的数据,需要重新解析一次。 + // Data that is outdated and needs to be parsed again. Object value = getValue(); if (value == null) return getDefaultValue(); // 获取的值不存在,直接使用默认值。 try { - // 若未出现错误,则直接更新缓存并返回。 + // If there are no errors, update the cache and return. return updateCache(this.parser.parse(value, this.defaultValue)); } catch (Exception e) { - // 出现了解析错误,提示并返回默认值。 + // There was a parsing error, prompted and returned the default value. e.printStackTrace(); return getDefaultValue(); } } + /** + * Set the value of the configuration path. + * Will use {@link #getSerializer()} to serialize the value. + * + * @param value The value to be set + */ @Override public void set(V value) { updateCache(value); 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 cfce9fa..d288459 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java @@ -1,6 +1,7 @@ package cc.carm.lib.configuration.demo.tests.conf; import cc.carm.lib.configuration.core.ConfigInitializer; +import cc.carm.lib.configuration.core.Configuration; import cc.carm.lib.configuration.core.ConfigurationRoot; import cc.carm.lib.configuration.core.annotation.ConfigPath; import cc.carm.lib.configuration.core.annotation.HeaderComment; @@ -18,35 +19,33 @@ import java.util.UUID; @HeaderComment({"此处内容将显示在配置文件的最上方"}) -public class DemoConfiguration extends ConfigurationRoot { +public interface DemoConfiguration extends Configuration { @ConfigPath(root = true) - protected static final ConfigValue VERSION = ConfiguredValue.of(Double.class, 1.0D); + ConfigValue VERSION = ConfiguredValue.of(Double.class, 1.0D); @ConfigPath(root = true) - public static final ConfigValue TEST_NUMBER = ConfiguredValue.of(Long.class, 1000000L); + ConfigValue TEST_NUMBER = ConfiguredValue.of(1000000L); - public static final ConfigValue TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS); + ConfigValue TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS); // 支持通过 Class 变量标注子配置,一并注册。 // 注意: 若对应类也有注解,则优先使用类的注解。 @ConfigPath("other-class-config") //支持通过注解修改子配置的主路径,若不修改则以变量名自动生成。 @HeaderComment({"", "Something..."}) // 支持给子路径直接打注释 @InlineComment("InlineComments for class path") - public static final Class OTHER = OtherConfiguration.class; + Class OTHER = OtherConfiguration.class; @ConfigPath("user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。 @HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。 @InlineComment("Section数据也支持InlineComment注释") - public static final ConfigValue MODEL_TEST = ConfiguredSection - .builderOf(TestModel.class) + ConfigValue MODEL_TEST = ConfiguredSection.builderOf(TestModel.class) .defaults(new TestModel("Carm", UUID.randomUUID())) .parseValue((section, defaultValue) -> TestModel.deserialize(section)) .serializeValue(TestModel::serialize).build(); @HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"}) - public static final ConfiguredMap USERS = ConfiguredMap - .builderOf(Integer.class, UUID.class) + ConfiguredMap USERS = ConfiguredMap.builderOf(Integer.class, UUID.class) .asLinkedMap().fromString() .parseKey(Integer::parseInt) .parseValue(v -> Objects.requireNonNull(UUID.fromString(v))) @@ -57,7 +56,7 @@ public class DemoConfiguration extends ConfigurationRoot { * 支持内部类的直接注册。 * 注意,需要使用 {@link ConfigInitializer#initialize(Class, boolean, boolean)} 方法,并设定第三个参数为 true。 */ - public static class Sub extends ConfigurationRoot { + class Sub extends ConfigurationRoot { @ConfigPath(value = "uuid-value", root = true) @InlineComment("This is an inline comment") diff --git a/impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/HOCONTest.java b/impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/HOCONTest.java index f960613..716df5c 100644 --- a/impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/HOCONTest.java +++ b/impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/HOCONTest.java @@ -1,10 +1,8 @@ package online.flowerinsnow.test.easyconfiguration; import cc.carm.lib.configuration.EasyConfiguration; -//import cc.carm.lib.configuration.demo.DatabaseConfiguration; -//import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration; import cc.carm.lib.configuration.hocon.HOCONFileConfigProvider; -import online.flowerinsnow.test.easyconfiguration.config.Config; +import online.flowerinsnow.test.easyconfiguration.config.SimpleConfig; import org.junit.Test; import java.io.File; @@ -13,9 +11,15 @@ public class HOCONTest { @Test public void onTest() { HOCONFileConfigProvider provider = EasyConfiguration.from(new File("target/hocon.conf")); - provider.initialize(Config.class); + provider.initialize(SimpleConfig.class); // provider.initialize(DatabaseConfiguration.class); + + System.out.println(SimpleConfig.TEST_INT.getNotNull()); + SimpleConfig.TEST_INT.set((int) (Math.random() * 100 * 5)); + System.out.println(SimpleConfig.TEST_INT.getNotNull()); + try { + provider.save(); provider.reload(); } catch (Exception e) { e.printStackTrace(); diff --git a/impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/config/Config.java b/impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/config/SimpleConfig.java similarity index 53% rename from impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/config/Config.java rename to impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/config/SimpleConfig.java index 68bc525..9201a6b 100644 --- a/impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/config/Config.java +++ b/impl/hocon/src/test/java/online/flowerinsnow/test/easyconfiguration/config/SimpleConfig.java @@ -1,25 +1,28 @@ package online.flowerinsnow.test.easyconfiguration.config; -import cc.carm.lib.configuration.core.ConfigurationRoot; +import cc.carm.lib.configuration.core.Configuration; import cc.carm.lib.configuration.core.annotation.HeaderComment; import cc.carm.lib.configuration.core.value.type.ConfiguredList; import cc.carm.lib.configuration.core.value.type.ConfiguredValue; -public class Config extends ConfigurationRoot { +public interface SimpleConfig extends Configuration { @HeaderComment("测试字段 int") - public static final ConfiguredValue TEST_INT = ConfiguredValue.of(Integer.class, 15); + ConfiguredValue TEST_INT = ConfiguredValue.of(Integer.class, 15); @HeaderComment("测试字段 List") - public static final ConfiguredList TEST_LIST_STRING = ConfiguredList.of(String.class, "li", "li", "li1"); + ConfiguredList TEST_LIST_STRING = ConfiguredList.of(String.class, "li", "li", "li1"); @HeaderComment("测试对象") - public static class TestObject extends ConfigurationRoot { + interface TestObject extends Configuration { + @HeaderComment("测试字段 Boolean") - public static final ConfiguredValue TEST_BOOLEAN = ConfiguredValue.of(Boolean.class, true); + ConfiguredValue TEST_BOOLEAN = ConfiguredValue.of(Boolean.class, true); + @HeaderComment("inner") - public static class InnerObject extends ConfigurationRoot { + interface InnerObject extends Configuration { @HeaderComment("测试字段") public static final ConfiguredValue TEST_BOOLEAN_1 = ConfiguredValue.of(Boolean.class, true); } + } }