From 47e2a4854c34d1a36a3f703280fd2223e2b169ef Mon Sep 17 00:00:00 2001 From: Carm Date: Thu, 13 Feb 2025 06:48:58 +0800 Subject: [PATCH] feat(yaml): Finished YAML Provider --- .../configuration/function/DataConsumer.java | 18 ++++ .../source/ConfigurationFactory.java | 24 +++-- .../loader/ConfigurationInitializer.java | 28 ++++-- .../source/loader/PathGenerator.java | 13 +-- .../source/option/ConfigurationOption.java | 6 ++ .../source/section/ConfigureSource.java | 5 ++ .../lib/configuration/value/ConfigValue.java | 88 ++++++++++++++----- .../value/standard/ConfiguredList.java | 32 +++---- core/src/test/java/AdaptTest.java | 57 ------------ .../java/cc/carm/test/config/LoaderTest.java | 55 ------------ .../java/cc/carm/test/config/TestSection.java | 57 ------------ .../java/cc/carm/test/config/TestSource.java | 45 ---------- .../demo/tests/ConfigurationTest.java | 4 +- .../source/file/FileConfigSource.java | 35 +++++++- features/section/pom.xml | 51 +++++++++++ .../source/section/MemorySection.java | 64 ++++++++------ .../commentable/VersionedMetaTypes.java | 2 +- pom.xml | 3 +- providers/gson/pom.xml | 7 ++ .../source/json/JSONConfigFactory.java | 11 ++- .../configuration/source/json/JSONSource.java | 43 ++++----- providers/yaml/pom.xml | 14 +++ .../source/yaml/YAMLSection.java | 76 ---------------- .../yaml/src/test/java/sample/Sample.java | 15 ---- .../src/test/java/sample/SampleConfig.java | 6 +- .../yaml/src/test/java/sample/SampleTest.java | 24 +++++ .../test2/config.yml => configs/sample.yml} | 0 27 files changed, 357 insertions(+), 426 deletions(-) create mode 100644 core/src/main/java/cc/carm/lib/configuration/function/DataConsumer.java delete mode 100644 core/src/test/java/AdaptTest.java delete mode 100644 core/src/test/java/cc/carm/test/config/LoaderTest.java delete mode 100644 core/src/test/java/cc/carm/test/config/TestSection.java delete mode 100644 core/src/test/java/cc/carm/test/config/TestSource.java create mode 100644 features/section/pom.xml rename providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSection.java => features/section/src/main/java/cc/carm/lib/configuration/source/section/MemorySection.java (65%) delete mode 100644 providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSection.java delete mode 100644 providers/yaml/src/test/java/sample/Sample.java create mode 100644 providers/yaml/src/test/java/sample/SampleTest.java rename providers/yaml/src/test/resources/{test/test2/config.yml => configs/sample.yml} (100%) diff --git a/core/src/main/java/cc/carm/lib/configuration/function/DataConsumer.java b/core/src/main/java/cc/carm/lib/configuration/function/DataConsumer.java new file mode 100644 index 0000000..69a5fca --- /dev/null +++ b/core/src/main/java/cc/carm/lib/configuration/function/DataConsumer.java @@ -0,0 +1,18 @@ +package cc.carm.lib.configuration.function; + +import org.jetbrains.annotations.NotNull; + +@FunctionalInterface +public interface DataConsumer { + + void accept(@NotNull T data) throws Exception; + + default DataConsumer andThen(DataConsumer after) { + return (T t) -> { + accept(t); + after.accept(t); + }; + } + + +} 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 8d93f88..ceabbef 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 @@ -15,6 +15,7 @@ import org.jetbrains.annotations.NotNull; import java.lang.annotation.Annotation; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Supplier; public abstract class ConfigurationFactory< SOURCE extends ConfigureSource, @@ -31,8 +32,7 @@ public abstract class ConfigurationFactory< this.adapters.register(StandardAdapters.SECTION_ADAPTER); } - public abstract SELF self(); - + protected abstract SELF self(); public SELF adapters(ValueAdapterRegistry adapters) { this.adapters = adapters; @@ -81,13 +81,25 @@ public abstract class ConfigurationFactory< return self(); } - public SELF option(Consumer optionsConsumer) { - optionsConsumer.accept(options); + public SELF option(Consumer modifier) { + modifier.accept(options); return self(); } - public SELF option(ConfigurationOption option, O value) { - return option(o -> o.set(option, value)); + public SELF option(ConfigurationOption type, O value) { + return option(o -> o.set(type, value)); + } + + public SELF option(ConfigurationOption type, Supplier value) { + return option(type, value.get()); + } + + public SELF option(ConfigurationOption type, Consumer modifier) { + return option(holder -> { + O current = holder.get(type); + modifier.accept(current); + holder.set(type, current); + }); } 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 a6f7f42..d49cdd2 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 @@ -67,14 +67,9 @@ public class ConfigurationInitializer { this.classInitializer = this.classInitializer.andThen(classInitializer); } - public void registerAnnotation(@NotNull Class annotation, - @NotNull ConfigurationMetadata metadata, - @NotNull Function extractor) { - appendFieldInitializer((holder, path, field) -> { - A data = field.getAnnotation(annotation); - if (data == null) return; - holder.metadata(path).setIfAbsent(metadata, extractor.apply(data)); - }); + public void registerClassAnnotation(@NotNull Class annotation, + @NotNull ConfigurationMetadata metadata, + @NotNull Function extractor) { appendClassInitializer((holder, path, clazz) -> { A data = clazz.getAnnotation(annotation); if (data == null) return; @@ -82,6 +77,23 @@ public class ConfigurationInitializer { }); } + public void registerFieldAnnotation(@NotNull Class annotation, + @NotNull ConfigurationMetadata metadata, + @NotNull Function extractor) { + appendFieldInitializer((holder, path, field) -> { + A data = field.getAnnotation(annotation); + if (data == null) return; + holder.metadata(path).setIfAbsent(metadata, extractor.apply(data)); + }); + } + + public void registerAnnotation(@NotNull Class annotation, + @NotNull ConfigurationMetadata metadata, + @NotNull Function extractor) { + registerClassAnnotation(annotation, metadata, extractor); + registerFieldAnnotation(annotation, metadata, extractor); + } + public @Nullable String getFieldPath(@NotNull ConfigurationHolder holder, @Nullable String parentPath, @NotNull Field field) { return pathGenerator.getFieldPath(holder, parentPath, field); diff --git a/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java b/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java index fc2e260..827fc69 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/loader/PathGenerator.java @@ -54,13 +54,14 @@ public class PathGenerator { ConfigPath clazzPath = clazz.getAnnotation(ConfigPath.class); if (clazzPath != null) return link(holder, parentPath, clazzPath.root(), clazzPath.value()); - if (clazzField == null) { - return link(holder, parentPath, false, parentPath == null ? null : covertPath(clazz.getSimpleName())); // No field, use class name. + if (clazzField != null) { + ConfigPath fieldPath = clazzField.getAnnotation(ConfigPath.class); + if (fieldPath == null) return link(holder, parentPath, false, covertPath(clazzField.getName())); + else return getFieldPath(holder, parentPath, clazzField); } - ConfigPath fieldPath = clazzField.getAnnotation(ConfigPath.class); - if (fieldPath == null) return link(holder, parentPath, false, covertPath(clazzField.getName())); - else return getFieldPath(holder, parentPath, clazzField); + return link(holder, parentPath, false, covertPath(clazz.getSimpleName())); // No field, use class name. + } protected String select(String path, String defaultValue) { @@ -71,7 +72,7 @@ public class PathGenerator { protected @Nullable String link(@NotNull ConfigurationHolder holder, @Nullable String parent, boolean root, @Nullable String path) { if (path == null || path.isEmpty()) return root ? null : parent; - return root || parent == null ? path : parent + pathSeparator(holder) + path; + return (root || parent == null) ? path : (parent + pathSeparator(holder) + path); } public static boolean isBlank(String path) { diff --git a/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOption.java b/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOption.java index fdc6ed4..4f6068d 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOption.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/option/ConfigurationOption.java @@ -2,6 +2,8 @@ package cc.carm.lib.configuration.source.option; import org.jetbrains.annotations.NotNull; +import java.util.function.Supplier; + public class ConfigurationOption { @SuppressWarnings("unchecked") @@ -13,6 +15,10 @@ public class ConfigurationOption { return new ConfigurationOption<>(valueClazz, defaultValue); } + public static ConfigurationOption of(@NotNull Supplier defaultValue) { + return of(defaultValue.get()); + } + private final @NotNull Class valueClazz; private @NotNull V defaultValue; diff --git a/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSource.java b/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSource.java index f0c83c7..1780105 100644 --- a/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSource.java +++ b/core/src/main/java/cc/carm/lib/configuration/source/section/ConfigureSource.java @@ -59,6 +59,11 @@ public abstract class ConfigureSource< return null; // Source also represents the root section, so it has no parent } + @Override + public @NotNull ConfigureSource source() { + return self(); + } + @Override public @NotNull Map getValues(boolean deep) { return section().getValues(deep); 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 70557d6..7b7075a 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 @@ -7,6 +7,32 @@ import org.jetbrains.annotations.Nullable; import java.util.Objects; import java.util.Optional; +/** + * Represents a configurable value with type safety and null-handling capabilities. + *

+ * This abstract class provides core functionalities for managing configuration values, + * including value retrieval with fallback defaults, null safety enforcement, and value + * persistence controls. It serves as the foundation for type-specific configuration + * implementations. + *

+ * + *

Functions:

+ *
    + *
  • Type-safe value access through {@link #get()} and {@link #optional()}
  • + *
  • Default value fallback via {@link #getOrDefault()}
  • + *
  • Null-safety enforcement with {@link #resolve()} and {@link #getNotNull()}
  • + *
  • Default value initialization through {@link #setDefault()}
  • + *
  • Value comparison with {@link #isDefault()}
  • + *
+ * + *

Persistence Behavior:

+ * Value modifications via {@link #set(Object)} or {@link #setDefault()} methods + * do NOT automatically persist to configuration sources. Explicit calls to + * {@link ConfigurationHolder#save()} are required for permanent storage. + * + * @see ValueManifest Base class providing metadata and default value handling + * @see ConfigurationHolder Responsible for configuration source persistence + */ public abstract class ConfigValue extends ValueManifest { protected ConfigValue(@NotNull ValueManifest manifest) { @@ -14,57 +40,77 @@ public abstract class ConfigValue extends ValueManifest { } /** - * 得到该配置的设定值(即读取到的值)。 - *
若初始化时未写入默认值,则可以通过 {@link #getOrDefault()} 方法在该设定值为空时获取默认值。 + * Gets the configured value (i.e., the value read from the source). + *
If no default value was written during initialization, you can use + * the {@link #getOrDefault()} method to obtain the default value when this value is empty. * - * @return 设定值 + * @return Configured value */ public abstract @Nullable T get(); /** - * 得到该配置的设定值,若不存在,则返回默认值。 + * Gets the configured value, or returns the default value if not present. * - * @return 设定值或默认值 + * @return Configured value or default value */ public T getOrDefault() { return optional().orElse(defaults()); } /** - * 得到该配置的非空值。 + * Gets the non-null value of this configuration. * - * @return 非空值 - * @throws NullPointerException 对应数据为空时抛出 + * @return Non-null value + * @throws NullPointerException Thrown when the corresponding data is null */ - public @NotNull T getNotNull() { + public @NotNull T resolve() { return Objects.requireNonNull(getOrDefault(), "Value(" + type() + ") @[" + path() + "] is null."); } + /** + * Gets the non-null value of this configuration. + * + * @return Non-null value + * @throws NullPointerException Thrown when the corresponding data is null + * @see #resolve() + */ + public @NotNull T getNotNull() { + return resolve(); + } + + /** + * Gets the value of this configuration as an {@link Optional}. + * + * @return {@link Optional} value + */ public @NotNull Optional<@Nullable T> optional() { return Optional.ofNullable(get()); } /** - * 设定该配置的值。 - *
设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationHolder#save()} 方法。 + * Sets the value of this configuration. + *
After setting, the configuration file will NOT be saved automatically. + * To save, call {@link ConfigurationHolder#save()}. * - * @param value 配置的值 + * @param value The value to set */ public abstract void set(@Nullable T value); /** - * 初始化该配置的默认值。 - *
设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationHolder#save()} 方法。 + * Initializes the default value for this configuration. + *
After setting, the configuration file will NOT be saved automatically. + * To save, call {@link ConfigurationHolder#save()}. */ public void setDefault() { setDefault(false); } /** - * 将该配置的值设置为默认值。 - *
设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationHolder#save()} 方法。 + * Sets the configuration value to its default. + *
After setting, the configuration file will NOT be saved automatically. + * To save, call {@link ConfigurationHolder#save()}. * - * @param override 是否覆盖已设定的值 + * @param override Whether to overwrite existing configured value */ public void setDefault(boolean override) { if (!override && config().contains(path())) return; @@ -72,9 +118,9 @@ public abstract class ConfigValue extends ValueManifest { } /** - * 判断加载的配置是否与默认值相同。 + * Checks if the loaded configuration value matches the default value. * - * @return 获取当前值是否为默认值。 + * @return Whether the current value is the default value */ public boolean isDefault() { return Objects.equals(defaults(), get()); @@ -86,10 +132,10 @@ public abstract class ConfigValue extends ValueManifest { * it is recommended to call {@link ConfigurationHolder#save()} * after all modifications are completed instead of this. * - * @throws Exception + * @throws Exception Thrown when an error occurs during saving */ public void save() throws Exception { holder().save(); } -} +} \ No newline at end of file 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 5cebea2..26c1f43 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 @@ -111,22 +111,22 @@ public class ConfiguredList extends CachedConfigValue> implements Lis @Override public V get(int index) { - return getNotNull().get(index); + return resolve().get(index); } public @NotNull List copy() { - return new ArrayList<>(getNotNull()); + return new ArrayList<>(resolve()); } public @NotNull T handle(Function, T> function) { - List list = getNotNull(); + List list = resolve(); T result = function.apply(list); set(list); return result; } public @NotNull ConfiguredList modify(Consumer> consumer) { - List list = getNotNull(); + List list = resolve(); consumer.accept(list); set(list); return this; @@ -139,40 +139,40 @@ public class ConfiguredList extends CachedConfigValue> implements Lis @Override public int size() { - return getNotNull().size(); + return resolve().size(); } @Override public boolean isEmpty() { - return getNotNull().isEmpty(); + return resolve().isEmpty(); } @Override public boolean contains(Object o) { - return getNotNull().contains(o); + return resolve().contains(o); } @NotNull @Override public Iterator iterator() { - return getNotNull().iterator(); + return resolve().iterator(); } @NotNull @Override public Object @NotNull [] toArray() { - return getNotNull().toArray(); + return resolve().toArray(); } @NotNull @Override public T @NotNull [] toArray(@NotNull T[] a) { - return getNotNull().toArray(a); + return resolve().toArray(a); } @Override public boolean containsAll(@NotNull Collection c) { - return new HashSet<>(getNotNull()).containsAll(c); + return new HashSet<>(resolve()).containsAll(c); } @Override @@ -223,30 +223,30 @@ public class ConfiguredList extends CachedConfigValue> implements Lis @Override public int indexOf(Object o) { - return getNotNull().indexOf(o); + return resolve().indexOf(o); } @Override public int lastIndexOf(Object o) { - return getNotNull().lastIndexOf(o); + return resolve().lastIndexOf(o); } @NotNull @Override public ListIterator listIterator() { - return getNotNull().listIterator(); + return resolve().listIterator(); } @NotNull @Override public ListIterator listIterator(int index) { - return getNotNull().listIterator(index); + return resolve().listIterator(index); } @NotNull @Override public List subList(int fromIndex, int toIndex) { - return getNotNull().subList(fromIndex, toIndex); + return resolve().subList(fromIndex, toIndex); } } \ No newline at end of file diff --git a/core/src/test/java/AdaptTest.java b/core/src/test/java/AdaptTest.java deleted file mode 100644 index bf55bcc..0000000 --- a/core/src/test/java/AdaptTest.java +++ /dev/null @@ -1,57 +0,0 @@ -import cc.carm.lib.configuration.adapter.ValueAdapterRegistry; -import cc.carm.lib.configuration.adapter.ValueType; -import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter; -import cc.carm.lib.configuration.source.ConfigurationHolder; -import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; -import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder; -import cc.carm.test.config.TestSource; -import org.jetbrains.annotations.NotNull; -import org.junit.Test; - -import java.time.Duration; -import java.time.LocalTime; -import java.util.concurrent.ConcurrentHashMap; - -public class AdaptTest { - - @Test - public void test() throws Exception { - - ValueAdapterRegistry registry = new ValueAdapterRegistry(); - registry.register(PrimitiveAdapter.ADAPTERS); - registry.register(PrimitiveAdapter.ofEnum()); - - - registry.register(ValueType.of(Long.class), ValueType.of(Duration.class), Duration::ofMillis, Duration::toMillis); - registry.register( - ValueType.of(Duration.class), ValueType.of(LocalTime.class), - duration -> LocalTime.now().plus(duration), - data -> Duration.between(LocalTime.now(), data) - ); - - ConfigurationHolder provider = new ConfigurationHolder( - registry, new ConfigurationOptionHolder(), - new ConcurrentHashMap<>(), new ConfigurationInitializer() - ) { - final TestSource source = new TestSource(this, System.currentTimeMillis()); - - @Override - public @NotNull TestSource config() { - return source; - } - }; - - LocalTime v = registry.deserialize(provider, LocalTime.class, 600000L); - Object d = registry.serialize(provider, v); - - System.out.println(v); - System.out.println(d); - System.out.println(registry.deserialize(provider, TestEnum.class, "C")); - System.out.println(registry.serialize(provider, TestEnum.C).getClass()); - } - - enum TestEnum { - A, b, C - } - -} diff --git a/core/src/test/java/cc/carm/test/config/LoaderTest.java b/core/src/test/java/cc/carm/test/config/LoaderTest.java deleted file mode 100644 index fdffa23..0000000 --- a/core/src/test/java/cc/carm/test/config/LoaderTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package cc.carm.test.config; - -import cc.carm.lib.configuration.adapter.ValueAdapterRegistry; -import cc.carm.lib.configuration.annotation.ConfigPath; -import cc.carm.lib.configuration.Configuration; -import cc.carm.lib.configuration.source.ConfigurationHolder; -import cc.carm.lib.configuration.source.loader.ConfigurationInitializer; -import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder; -import org.jetbrains.annotations.NotNull; -import org.junit.Test; - -import java.util.concurrent.ConcurrentHashMap; - -public class LoaderTest { - - @Test - public void test() throws Exception { - ConfigurationHolder provider = new ConfigurationHolder( - new ValueAdapterRegistry(), new ConfigurationOptionHolder(), - new ConcurrentHashMap<>(), new ConfigurationInitializer() - ) { - final TestSource source = new TestSource(this, System.currentTimeMillis()); - - @Override - public @NotNull TestSource config() { - return source; - } - }; - - ConfigurationInitializer loader = new ConfigurationInitializer(); - loader.initialize(provider, ROOT.class); - } - - interface ROOT extends Configuration { - - interface SUB extends Configuration { - - - } - - - @ConfigPath(root = true) - interface EXTERNAL extends Configuration { - - - } - - @ConfigPath("NO") - interface YES extends Configuration { - - - } - - } -} \ No newline at end of file diff --git a/core/src/test/java/cc/carm/test/config/TestSection.java b/core/src/test/java/cc/carm/test/config/TestSection.java deleted file mode 100644 index 2293a0e..0000000 --- a/core/src/test/java/cc/carm/test/config/TestSection.java +++ /dev/null @@ -1,57 +0,0 @@ -package cc.carm.test.config; - -import cc.carm.lib.configuration.source.section.ConfigureSection; -import cc.carm.lib.configuration.source.section.ConfigureSource; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class TestSection implements ConfigureSection { - @Override - public @NotNull ConfigureSource source() { - return null; - } - - @Override - public @NotNull Map getValues(boolean deep) { - return Collections.emptyMap(); - } - - @Override - public void set(@NotNull String path, @Nullable Object value) { - - } - - @Override - public boolean contains(@NotNull String path) { - return false; - } - - @Override - public boolean isList(@NotNull String path) { - return false; - } - - @Override - public @Nullable List getList(@NotNull String path) { - return Collections.emptyList(); - } - - @Override - public boolean isSection(@NotNull String path) { - return false; - } - - @Override - public @Nullable ConfigureSection getSection(@NotNull String path) { - return null; - } - - @Override - public @Nullable Object get(@NotNull String path) { - return null; - } -} diff --git a/core/src/test/java/cc/carm/test/config/TestSource.java b/core/src/test/java/cc/carm/test/config/TestSource.java deleted file mode 100644 index 6a1d5e9..0000000 --- a/core/src/test/java/cc/carm/test/config/TestSource.java +++ /dev/null @@ -1,45 +0,0 @@ -package cc.carm.test.config; - -import cc.carm.lib.configuration.source.ConfigurationHolder; -import cc.carm.lib.configuration.source.section.ConfigureSource; -import org.jetbrains.annotations.NotNull; - -import java.util.Map; - -public class TestSource extends ConfigureSource, TestSource> { - - - public TestSource(@NotNull ConfigurationHolder holder, long lastUpdateMillis) { - super(holder, lastUpdateMillis); - } - - @Override - protected TestSource self() { - return this; - } - - @Override - public void save() throws Exception { - - } - - @Override - protected void onReload() throws Exception { - - } - - @Override - public @NotNull Map original() { - return null; - } - - @Override - public @NotNull TestSection section() { - return null; - } - - @Override - public @NotNull ConfigureSource source() { - return null; - } -} diff --git a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/ConfigurationTest.java b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/ConfigurationTest.java index 1f52303..6fad2f6 100644 --- a/demo/src/main/java/cc/carm/lib/configuration/demo/tests/ConfigurationTest.java +++ b/demo/src/main/java/cc/carm/lib/configuration/demo/tests/ConfigurationTest.java @@ -69,14 +69,14 @@ public class ConfigurationTest { provider.initialize(TEST); System.out.println("> Test Inner value before:"); - System.out.println(TEST.INSTANCE.INNER_VALUE.getNotNull()); + System.out.println(TEST.INSTANCE.INNER_VALUE.resolve()); double after = Math.random() * 200D; System.out.println("> Test Inner value -> " + after); TEST.INSTANCE.INNER_VALUE.set(after); System.out.println("> Test Inner value after:"); - System.out.println(TEST.INSTANCE.INNER_VALUE.getNotNull()); + System.out.println(TEST.INSTANCE.INNER_VALUE.resolve()); } diff --git a/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigSource.java b/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigSource.java index 85b7013..050e9ab 100644 --- a/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigSource.java +++ b/features/file/src/main/java/cc/carm/lib/configuration/source/file/FileConfigSource.java @@ -1,5 +1,6 @@ package cc.carm.lib.configuration.source.file; +import cc.carm.lib.configuration.function.DataConsumer; import cc.carm.lib.configuration.function.DataFunction; import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.option.FileConfigOptions; @@ -14,7 +15,6 @@ import java.net.URLConnection; import java.nio.charset.Charset; import java.nio.file.Files; import java.util.Objects; -import java.util.function.Consumer; public abstract class FileConfigSource
> extends ConfigureSource { @@ -38,7 +38,6 @@ public abstract class FileConfigSource
stream) throws Exception { + protected R fileReadString(@NotNull DataFunction loader) throws Exception { + try (InputStream is = Files.newInputStream(file.toPath())) { + try (Reader r = new InputStreamReader(is, charset())) { + StringBuilder sb = new StringBuilder(); + char[] buf = new char[1024]; + int len; + while ((len = r.read(buf)) > 0) { + sb.append(buf, 0, len); + } + return loader.handle(sb.toString()); + } + } + } + + protected void fileReadString(@NotNull DataConsumer loader) throws Exception { + try (InputStream is = Files.newInputStream(file.toPath())) { + try (Reader r = new InputStreamReader(is, charset())) { + StringBuilder sb = new StringBuilder(); + char[] buf = new char[1024]; + int len; + while ((len = r.read(buf)) > 0) { + sb.append(buf, 0, len); + } + loader.accept(sb.toString()); + } + } + } + + protected void fileOutputStream(@NotNull DataConsumer stream) throws Exception { try (OutputStream os = Files.newOutputStream(file.toPath())) { stream.accept(os); } } - protected void fileWriter(@NotNull Consumer writer) throws Exception { + protected void fileWriter(@NotNull DataConsumer writer) throws Exception { try (OutputStream os = Files.newOutputStream(file.toPath())) { try (Writer w = new OutputStreamWriter(os, charset())) { writer.accept(w); diff --git a/features/section/pom.xml b/features/section/pom.xml new file mode 100644 index 0000000..9a11655 --- /dev/null +++ b/features/section/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + cc.carm.lib + easyconfiguration-parent + 3.9.1 + ../../pom.xml + + + ${project.jdk.version} + ${project.jdk.version} + UTF-8 + UTF-8 + + + easyconfiguration-feature-section + jar + + + + ${project.groupId} + easyconfiguration-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/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSection.java b/features/section/src/main/java/cc/carm/lib/configuration/source/section/MemorySection.java similarity index 65% rename from providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSection.java rename to features/section/src/main/java/cc/carm/lib/configuration/source/section/MemorySection.java index 3b93a2c..c171629 100644 --- a/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSection.java +++ b/features/section/src/main/java/cc/carm/lib/configuration/source/section/MemorySection.java @@ -1,34 +1,39 @@ -package cc.carm.lib.configuration.source.json; +package cc.carm.lib.configuration.source.section; -import cc.carm.lib.configuration.source.section.ConfigureSection; -import cc.carm.lib.configuration.source.section.ConfigureSource; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -public class JSONSection implements ConfigureSection { +public class MemorySection implements ConfigureSection { - protected final @NotNull ConfigureSource source; + public static @NotNull MemorySection root(@NotNull ConfigureSource source) { + return new MemorySection(source, new LinkedHashMap<>(), null); + } + + public static @NotNull MemorySection root(@NotNull ConfigureSource source, + @Nullable Map data) { + return new MemorySection(source, data == null ? new LinkedHashMap<>() : data, null); + } + + protected final @NotNull ConfigureSource source; protected final @NotNull Map data; - protected final @Nullable JSONSection parent; + protected final @Nullable MemorySection parent; - public JSONSection(@NotNull ConfigureSource source, - @NotNull Map data, @Nullable JSONSection parent) { + public MemorySection(@NotNull ConfigureSource source, + @NotNull Map data, @Nullable MemorySection parent) { this.source = source; this.parent = parent; this.data = new LinkedHashMap<>(); - for (Map.Entry entry : data.entrySet()) { String key = (entry.getKey() == null) ? "null" : entry.getKey().toString(); - if (entry.getValue() instanceof Map) { - this.data.put(key, new JSONSection(source, (Map) entry.getValue(), this)); + this.data.put(key, createChild((Map) entry.getValue())); } else if (entry.getValue() instanceof List) { List list = new ArrayList<>(); for (Object obj : (List) entry.getValue()) { if (obj instanceof Map) { - list.add(new JSONSection(source, (Map) obj, this)); + list.add(createChild((Map) obj)); } else { list.add(obj); } @@ -40,8 +45,16 @@ public class JSONSection implements ConfigureSection { } } + protected @NotNull MemorySection createChild(@NotNull Map data) { + return new MemorySection(source(), data, this); + } + + protected @NotNull MemorySection createChild() { + return createChild(new LinkedHashMap<>()); + } + @Override - public @NotNull ConfigureSource source() { + public @NotNull ConfigureSource source() { return this.source; } @@ -49,7 +62,7 @@ public class JSONSection implements ConfigureSection { return this.data; } - public @Nullable JSONSection parent() { + public @Nullable MemorySection parent() { return this.parent; } @@ -64,11 +77,9 @@ public class JSONSection implements ConfigureSection { @Override public void set(@NotNull String path, @Nullable Object value) { - if (value instanceof Map) { - value = new JSONSection(source(), (Map) value, this); - } + if (value instanceof Map) value = createChild((Map) value); - JSONSection section = getSectionFor(path); + MemorySection section = getSectionFor(path); if (section == this) { if (value == null) { this.data.remove(path); @@ -87,7 +98,7 @@ public class JSONSection implements ConfigureSection { @Override public @Nullable Object get(@NotNull String path) { - JSONSection section = getSectionFor(path); + MemorySection section = getSectionFor(path); return section == this ? data.get(path) : section.get(getChild(path)); } @@ -104,7 +115,7 @@ public class JSONSection implements ConfigureSection { @Override public boolean isSection(@NotNull String path) { - return get(path) instanceof JSONSection; + return get(path) instanceof ConfigureSection; } @Override @@ -113,18 +124,18 @@ public class JSONSection implements ConfigureSection { return (val instanceof ConfigureSection) ? (ConfigureSection) val : null; } - private JSONSection getSectionFor(String path) { + private MemorySection getSectionFor(String path) { int index = path.indexOf(separator()); if (index == -1) return this; String root = path.substring(0, index); Object section = this.data.get(root); if (section == null) { - section = new JSONSection(source(), new LinkedHashMap<>(), this); + section = createChild(); this.data.put(root, section); } - return (JSONSection) section; + return (MemorySection) section; } private String getChild(String path) { @@ -140,16 +151,15 @@ public class JSONSection implements ConfigureSection { * @param section The section to map the values from * @param parent The parent path * @param deep If the mapping should be deep - * @author md_5, sk89q */ - protected void mapChildrenValues(@NotNull Map output, @NotNull JSONSection section, + protected void mapChildrenValues(@NotNull Map output, @NotNull MemorySection section, @Nullable String parent, boolean deep) { for (Map.Entry entry : section.data().entrySet()) { String path = (parent == null ? "" : parent + separator()) + entry.getKey(); output.remove(path); output.put(path, entry.getValue()); - if (deep && entry.getValue() instanceof JSONSection) { - this.mapChildrenValues(output, (JSONSection) entry.getValue(), path, true); + if (deep && entry.getValue() instanceof MemorySection) { + this.mapChildrenValues(output, (MemorySection) entry.getValue(), path, true); } } } diff --git a/features/versioned/src/main/java/cc/carm/lib/configuration/commentable/VersionedMetaTypes.java b/features/versioned/src/main/java/cc/carm/lib/configuration/commentable/VersionedMetaTypes.java index 5b77346..b730606 100644 --- a/features/versioned/src/main/java/cc/carm/lib/configuration/commentable/VersionedMetaTypes.java +++ b/features/versioned/src/main/java/cc/carm/lib/configuration/commentable/VersionedMetaTypes.java @@ -19,7 +19,7 @@ public interface VersionedMetaTypes { } static void register(@NotNull ConfigurationInitializer initializer) { - initializer.registerAnnotation(ConfigVersion.class, VERSION, ConfigVersion::value); + initializer.registerFieldAnnotation(ConfigVersion.class, VERSION, ConfigVersion::value); } } diff --git a/pom.xml b/pom.xml index 2df4f9d..a09011e 100644 --- a/pom.xml +++ b/pom.xml @@ -18,8 +18,9 @@ 3.9.1 core - features/commentable + features/section features/file + features/commentable features/versioned providers/yaml diff --git a/providers/gson/pom.xml b/providers/gson/pom.xml index 7ed8361..0732fe1 100644 --- a/providers/gson/pom.xml +++ b/providers/gson/pom.xml @@ -34,6 +34,13 @@ compile + + ${project.parent.groupId} + easyconfiguration-feature-section + ${project.parent.version} + compile + + ${project.parent.groupId} easyconfiguration-demo diff --git a/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONConfigFactory.java b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONConfigFactory.java index d7f5e39..f286c01 100644 --- a/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONConfigFactory.java +++ b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONConfigFactory.java @@ -28,7 +28,7 @@ public class JSONConfigFactory extends FileConfigFactory gson); - } - public JSONConfigFactory gson(@NotNull Consumer builder) { return gson(() -> { GsonBuilder gsonBuilder = new GsonBuilder(); @@ -49,6 +45,9 @@ public class JSONConfigFactory extends FileConfigFactory gson); + } @Override public @NotNull ConfigurationHolder build() { @@ -59,7 +58,7 @@ public class JSONConfigFactory extends FileConfigFactory(this.adapters, this.options, new ConcurrentHashMap<>(), this.initializer) { - final JSONSource source = new JSONSource(this, 0, configFile, sourcePath, gson); + final JSONSource source = new JSONSource(this, configFile, sourcePath, gson); @Override public @NotNull JSONSource config() { diff --git a/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSource.java b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSource.java index 3354665..4ab433a 100644 --- a/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSource.java +++ b/providers/gson/src/main/java/cc/carm/lib/configuration/source/json/JSONSource.java @@ -2,36 +2,38 @@ package cc.carm.lib.configuration.source.json; import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.file.FileConfigSource; -import cc.carm.lib.configuration.source.section.ConfigureSource; +import cc.carm.lib.configuration.source.section.MemorySection; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonSerializer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.*; -import java.util.*; +import java.io.File; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; -public class JSONSource extends FileConfigSource, JSONSource> { +public class JSONSource extends FileConfigSource, JSONSource> { public static final @NotNull Gson DEFAULT_GSON = new GsonBuilder() .serializeNulls().disableHtmlEscaping().setPrettyPrinting() .registerTypeAdapter( - JSONSection.class, - (JsonSerializer) (src, typeOfSrc, context) -> context.serialize(src.data) + MemorySection.class, + (JsonSerializer) (src, t, c) -> c.serialize(src.data()) ).create(); protected final @NotNull Gson gson; - protected @Nullable JSONSection rootSection; + protected @Nullable MemorySection rootSection; - protected JSONSource(@NotNull ConfigurationHolder holder, long lastUpdateMillis, + protected JSONSource(@NotNull ConfigurationHolder holder, @NotNull File file, @Nullable String resourcePath) { - this(holder, lastUpdateMillis, file, resourcePath, DEFAULT_GSON); + this(holder, file, resourcePath, DEFAULT_GSON); } - protected JSONSource(@NotNull ConfigurationHolder holder, long lastUpdateMillis, + protected JSONSource(@NotNull ConfigurationHolder holder, @NotNull File file, @Nullable String resourcePath, @NotNull Gson gson) { - super(holder, lastUpdateMillis, file, resourcePath); + super(holder, 0, file, resourcePath); this.gson = gson; initialize(); } @@ -50,19 +52,14 @@ public class JSONSource extends FileConfigSource, JSONSou return this; } - @Override - public @NotNull ConfigureSource source() { - return this; - } - @Override public @NotNull Map original() { return section().data(); } @Override - public @NotNull JSONSection section() { - return Objects.requireNonNull(this.rootSection, "Section is not initialized"); + public @NotNull MemorySection section() { + return Objects.requireNonNull(this.rootSection, "Root section is not initialized"); } @Override @@ -70,11 +67,15 @@ public class JSONSource extends FileConfigSource, JSONSou fileWriter(writer -> gson.toJson(original(), writer)); } + public @NotNull String saveToString() { + return gson.toJson(original()); + } + @Override protected void onReload() throws Exception { - Map data = fileReader(reader -> gson.fromJson(reader, LinkedHashMap.class)); - if (data == null) data = new LinkedHashMap<>(); - this.rootSection = new JSONSection(this, data, null); + this.rootSection = MemorySection.root( + this, fileReader(reader -> gson.fromJson(reader, LinkedHashMap.class)) + ); this.lastUpdateMillis = System.currentTimeMillis(); // 更新时间 } } diff --git a/providers/yaml/pom.xml b/providers/yaml/pom.xml index 88ea416..f1bd5ed 100644 --- a/providers/yaml/pom.xml +++ b/providers/yaml/pom.xml @@ -42,6 +42,20 @@ compile + + ${project.parent.groupId} + easyconfiguration-feature-section + ${project.parent.version} + compile + + + + ${project.parent.groupId} + easyconfiguration-feature-commentable + ${project.parent.version} + compile + + org.yaml snakeyaml diff --git a/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSection.java b/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSection.java deleted file mode 100644 index d5cbc95..0000000 --- a/providers/yaml/src/main/java/cc/carm/lib/configuration/source/yaml/YAMLSection.java +++ /dev/null @@ -1,76 +0,0 @@ -package cc.carm.lib.configuration.source.yaml; - -import cc.carm.lib.configuration.source.section.ConfigureSection; -import cc.carm.lib.configuration.source.section.ConfigureSource; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.UnmodifiableView; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class YAMLSection implements ConfigureSection { - - protected final @NotNull ConfigureSource source; - protected final @NotNull Map data; - protected final @Nullable YAMLSection parent; - - public YAMLSection(@NotNull ConfigureSource source, - @NotNull Map data, @Nullable YAMLSection parent) { - this.source = source; - this.data = data; - this.parent = parent; - } - - - @Override - public @NotNull ConfigureSource source() { - return this.source; - } - - @Override - public @Nullable YAMLSection parent() { - return this.parent; - } - - @Override - public @NotNull @UnmodifiableView Map getValues(boolean deep) { - return Collections.emptyMap(); - } - - @Override - public void set(@NotNull String path, @Nullable Object value) { - - } - - @Override - public boolean contains(@NotNull String path) { - return false; - } - - @Override - public boolean isList(@NotNull String path) { - return false; - } - - @Override - public @Nullable List getList(@NotNull String path) { - return Collections.emptyList(); - } - - @Override - public boolean isSection(@NotNull String path) { - return false; - } - - @Override - public @Nullable YAMLSection getSection(@NotNull String path) { - return null; - } - - @Override - public @Nullable Object get(@NotNull String path) { - return null; - } -} diff --git a/providers/yaml/src/test/java/sample/Sample.java b/providers/yaml/src/test/java/sample/Sample.java deleted file mode 100644 index 9c458cd..0000000 --- a/providers/yaml/src/test/java/sample/Sample.java +++ /dev/null @@ -1,15 +0,0 @@ -package sample; - -public class Sample { - - public static void main(String[] args) { - // 1. Make a configuration provider from a file. - ConfigurationProvider provider = EasyConfiguration.from("config.yml"); - // 2. Initialize the configuration classes or instances. - provider.initialize(SampleConfig.class); - // 3. Enjoy using the configuration! - SampleConfig.ENABLED.set(false); - System.out.println("Your name is " + SampleConfig.INFO.NAME.getNotNull() + " !"); - } - -} diff --git a/providers/yaml/src/test/java/sample/SampleConfig.java b/providers/yaml/src/test/java/sample/SampleConfig.java index d8b53d1..01720f0 100644 --- a/providers/yaml/src/test/java/sample/SampleConfig.java +++ b/providers/yaml/src/test/java/sample/SampleConfig.java @@ -1,6 +1,6 @@ package sample; -import cc.carm.lib.configuration.source.Configuration; +import cc.carm.lib.configuration.Configuration; import cc.carm.lib.configuration.annotation.ConfigPath; import cc.carm.lib.configuration.annotation.HeaderComment; import cc.carm.lib.configuration.annotation.InlineComment; @@ -9,6 +9,7 @@ import cc.carm.lib.configuration.value.standard.ConfiguredValue; import java.util.UUID; +@ConfigPath(root = true) @HeaderComment("Configurations for sample") public interface SampleConfig extends Configuration { @@ -16,12 +17,13 @@ public interface SampleConfig extends Configuration { ConfiguredValue ENABLED = ConfiguredValue.of(true); ConfiguredList UUIDS = ConfiguredList.builderOf(UUID.class).fromString() - .parseValue(UUID::fromString).serializeValue(UUID::toString) + .parse(UUID::fromString).serialize(UUID::toString) .defaults( UUID.fromString("00000000-0000-0000-0000-000000000000"), UUID.fromString("00000000-0000-0000-0000-000000000001") ).build(); + @ConfigPath("info") // Custom path interface INFO extends Configuration { @HeaderComment("Configure your name!") // Header comment diff --git a/providers/yaml/src/test/java/sample/SampleTest.java b/providers/yaml/src/test/java/sample/SampleTest.java new file mode 100644 index 0000000..c80c4d0 --- /dev/null +++ b/providers/yaml/src/test/java/sample/SampleTest.java @@ -0,0 +1,24 @@ +package sample; + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory; +import org.junit.Test; + +public class SampleTest { + + @Test + public void test() { + // 1. Make a configuration provider from a file. + ConfigurationHolder holder = YAMLConfigFactory.from("target/config.yml") + .resourcePath("configs/sample.yml") + .indent(4) // Optional: Set the indentation of the configuration file. + .build(); + + // 2. Initialize the configuration classes or instances. + holder.initialize(SampleConfig.class); + // 3. Enjoy using the configuration! + SampleConfig.ENABLED.set(false); + System.out.println("Your name is " + SampleConfig.INFO.NAME.resolve() + " (age=" + SampleConfig.INFO.AGE.resolve() + ")!"); + } + +} diff --git a/providers/yaml/src/test/resources/test/test2/config.yml b/providers/yaml/src/test/resources/configs/sample.yml similarity index 100% rename from providers/yaml/src/test/resources/test/test2/config.yml rename to providers/yaml/src/test/resources/configs/sample.yml