1
mirror of https://github.com/CarmJos/EasyConfiguration.git synced 2024-09-19 20:25:51 +00:00

feat(interface): Support interface root configurations.

This commit is contained in:
Carm Jos 2023-12-23 22:24:34 +08:00
parent 60eed8a14d
commit db8b227317
10 changed files with 116 additions and 59 deletions

View File

@ -14,7 +14,7 @@ import java.util.List;
/**
* 配置文件类初始化方法
* 用于初始化 {@link ConfigurationRoot} 中的每个 {@link ConfigValue} 对象
* 用于初始化 {@link Configuration} 中的每个 {@link ConfigValue} 对象
*
* @param <T> {@link ConfigurationProvider} 配置文件的数据来源
* @author CarmJos
@ -30,21 +30,21 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
/**
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象
*
* @param clazz 配置文件类须继承于 {@link ConfigurationRoot}
* @param clazz 配置文件类须继承于 {@link Configuration}
* @param saveDefaults 是否写入默认值(默认为 true)
*/
public void initialize(@NotNull Class<? extends ConfigurationRoot> clazz, boolean saveDefaults) {
public void initialize(@NotNull Class<? extends Configuration> 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<? extends ConfigurationRoot> clazz,
public void initialize(@NotNull Class<? extends Configuration> clazz,
boolean saveDefaults, boolean loadSubClasses) {
initializeStaticClass(
clazz, null, null,
@ -61,21 +61,21 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
}
/**
* 初始化指定实例的所有 {@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<T extends ConfigurationProvider<?>> {
// 针对实例类的初始化方法
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<T extends ConfigurationProvider<?>> {
@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<T extends ConfigurationProvider<?>> {
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),

View File

@ -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 {
}

View File

@ -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 {
}

View File

@ -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<W extends ConfigurationWrapper<?>> {
/**
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象
*
* @param configClazz 配置文件类须继承于 {@link ConfigurationRoot}
* @param configClazz 配置文件类须继承于 {@link Configuration}
*/
public void initialize(Class<? extends ConfigurationRoot> configClazz) {
public void initialize(Class<? extends Configuration> configClazz) {
initialize(configClazz, true);
}
/**
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象
*
* @param configClazz 配置文件类须继承于 {@link ConfigurationRoot}
* @param configClazz 配置文件类须继承于 {@link Configuration}
* @param saveDefaults 是否写入默认值(默认为 true)
*/
public void initialize(Class<? extends ConfigurationRoot> configClazz, boolean saveDefaults) {
public void initialize(Class<? extends Configuration> 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<? extends ConfigurationRoot> configClazz, boolean saveDefaults, boolean loadSubClasses) {
public void initialize(Class<? extends Configuration> 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);
}

View File

@ -43,8 +43,7 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements Lis
@Override
public @NotNull List<V> get() {
if (!isExpired()) return getCachedOrDefault(new ArrayList<>());
// 已过时的数据需要重新解析一次
// Data that is outdated and needs to be parsed again.
List<V> list = new ArrayList<>();
List<?> data = getConfiguration().contains(getConfigPath()) ?
getConfiguration().getList(getConfigPath()) : null;

View File

@ -31,37 +31,55 @@ public class ConfiguredSection<V> extends CachedConfigValue<V> {
this.serializer = serializer;
}
/**
* @return Value's type class
*/
public @NotNull Class<V> getValueClass() {
return valueClass;
}
/**
* @return Value's parser, cast value from section.
*/
public @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> getParser() {
return parser;
}
/**
* @return Value's serializer, serialize value to section.
*/
public @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> 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);

View File

@ -14,6 +14,11 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
return builder().asValue(valueClass);
}
@SuppressWarnings("unchecked")
public static <V> ConfiguredValue<V> of(@NotNull V defaultValue) {
return of((Class<V>) defaultValue.getClass(), defaultValue);
}
public static <V> ConfiguredValue<V> of(Class<V> valueClass) {
return of(valueClass, null);
}
@ -36,32 +41,51 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
this.serializer = serializer;
}
/**
* @return Value's type class
*/
public @NotNull Class<V> getValueClass() {
return valueClass;
}
/**
* @return Value's parser, cast value from base object.
*/
public @NotNull ConfigValueParser<Object, V> getParser() {
return parser;
}
/**
* @return Value's serializer, serialize value to base object.
*/
public @NotNull ConfigDataFunction<V, Object> 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);

View File

@ -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<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
@ConfigPath(root = true)
public static final ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(Long.class, 1000000L);
ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(1000000L);
public static final ConfigValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS);
ConfigValue<ChronoUnit> 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<TestModel> MODEL_TEST = ConfiguredSection
.builderOf(TestModel.class)
ConfigValue<TestModel> 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<Integer, UUID> USERS = ConfiguredMap
.builderOf(Integer.class, UUID.class)
ConfiguredMap<Integer, UUID> 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")

View File

@ -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();

View File

@ -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<Integer> TEST_INT = ConfiguredValue.of(Integer.class, 15);
ConfiguredValue<Integer> TEST_INT = ConfiguredValue.of(Integer.class, 15);
@HeaderComment("测试字段 List<String>")
public static final ConfiguredList<String> TEST_LIST_STRING = ConfiguredList.of(String.class, "li", "li", "li1");
ConfiguredList<String> 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<Boolean> TEST_BOOLEAN = ConfiguredValue.of(Boolean.class, true);
ConfiguredValue<Boolean> TEST_BOOLEAN = ConfiguredValue.of(Boolean.class, true);
@HeaderComment("inner")
public static class InnerObject extends ConfigurationRoot {
interface InnerObject extends Configuration {
@HeaderComment("测试字段")
public static final ConfiguredValue<Boolean> TEST_BOOLEAN_1 = ConfiguredValue.of(Boolean.class, true);
}
}
}