mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-05 02:58:20 +08:00
feat(loader): Refactor loaders and metadata.
This commit is contained in:
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>cc.carm.lib</groupId>
|
||||||
|
<artifactId>easyconfiguration-parent</artifactId>
|
||||||
|
<version>3.9.1</version>
|
||||||
|
</parent>
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||||
|
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<artifactId>easyconfiguration-commentable</artifactId>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</project>
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package cc.carm.lib.configuration.commentable;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.annotation.HeaderComment;
|
||||||
|
import cc.carm.lib.configuration.annotation.InlineComment;
|
||||||
|
import cc.carm.lib.easyannotation.AnnotatedMetaType;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface CommentableMetaTypes {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration's {@link HeaderComment}
|
||||||
|
*/
|
||||||
|
AnnotatedMetaType<HeaderComment, List<String>> HEADER_COMMENT = AnnotatedMetaType.of(
|
||||||
|
HeaderComment.class, h -> h.value().length == 0 ? null : Arrays.asList(h.value())
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration's {@link InlineComment}
|
||||||
|
*/
|
||||||
|
AnnotatedMetaType<InlineComment, String> INLINE_COMMENT = AnnotatedMetaType.of(
|
||||||
|
InlineComment.class, c -> c.value().isEmpty() ? null : c.value()
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package cc.carm.lib.configuration.commentable;
|
||||||
|
|
||||||
|
import cc.carm.lib.easyoptions.OptionType;
|
||||||
|
|
||||||
|
import static cc.carm.lib.easyoptions.OptionType.of;
|
||||||
|
|
||||||
|
public interface CommentableOptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to keep modified comments in configuration,
|
||||||
|
* that means we only set comments for values that are not exists in configuration.
|
||||||
|
*/
|
||||||
|
OptionType<Boolean> KEEP_COMMENTS = OptionType.of(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to comment values name that are not exists in configuration and no default value offered.
|
||||||
|
* <br>If true, a value without default value is not exists in configuration, we will comment its name,
|
||||||
|
* <p>e.g. a value named "foo" without default value will be put as:
|
||||||
|
* <blockquote><pre>
|
||||||
|
* # Value comments
|
||||||
|
* # foo:
|
||||||
|
* </pre></blockquote>
|
||||||
|
*/
|
||||||
|
OptionType<Boolean> COMMENT_NO_DEFAULT = OptionType.of(true);
|
||||||
|
|
||||||
|
}
|
||||||
+1
-4
@@ -1,4 +1,4 @@
|
|||||||
package cc.carm.lib.configuration.core.source;
|
package cc.carm.lib.configuration.commentable;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -20,7 +20,6 @@ public class ConfigurationComments {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setHeaderComments(@Nullable String path, @Nullable List<String> comments) {
|
public void setHeaderComments(@Nullable String path, @Nullable List<String> comments) {
|
||||||
|
|
||||||
if (comments == null) {
|
if (comments == null) {
|
||||||
getHeaderComments().remove(path);
|
getHeaderComments().remove(path);
|
||||||
} else {
|
} else {
|
||||||
@@ -28,7 +27,6 @@ public class ConfigurationComments {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setInlineComment(@NotNull String path, @Nullable String comment) {
|
public void setInlineComment(@NotNull String path, @Nullable String comment) {
|
||||||
if (comment == null) {
|
if (comment == null) {
|
||||||
getInlineComments().remove(path);
|
getInlineComments().remove(path);
|
||||||
@@ -47,5 +45,4 @@ public class ConfigurationComments {
|
|||||||
return getInlineComments().get(path);
|
return getInlineComments().get(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,19 @@
|
|||||||
<artifactId>easyconfiguration-core</artifactId>
|
<artifactId>easyconfiguration-core</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cc.carm.lib</groupId>
|
||||||
|
<artifactId>easyoptions</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cc.carm.lib</groupId>
|
||||||
|
<artifactId>easyannotation</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package cc.carm.lib.configuration.adapter;
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.source.ConfigurationProvider;
|
import cc.carm.lib.configuration.source.ConfigurationProvider;
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value adapter, used to convert the value of the configuration file into the objects.
|
* Value adapter, used to convert the value of the configuration file into the objects.
|
||||||
@@ -10,7 +9,8 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
* @param <B> The type of the base data
|
* @param <B> The type of the base data
|
||||||
* @param <V> The type of the target value
|
* @param <V> The type of the target value
|
||||||
*/
|
*/
|
||||||
public abstract class ValueAdapter<P extends ConfigurationProvider, B, V> {
|
public abstract class ValueAdapter<P extends ConfigurationProvider, B, V>
|
||||||
|
implements ValueSerializer<P, B, V>, ValueDeserializer<P, B, V> {
|
||||||
|
|
||||||
protected final Class<? super B> baseType;
|
protected final Class<? super B> baseType;
|
||||||
protected final Class<? super V> valueType;
|
protected final Class<? super V> valueType;
|
||||||
@@ -28,10 +28,6 @@ public abstract class ValueAdapter<P extends ConfigurationProvider, B, V> {
|
|||||||
return valueType;
|
return valueType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract B serialize(@NotNull P provider, @NotNull V value) throws Exception;
|
|
||||||
|
|
||||||
public abstract V deserialize(@NotNull P provider, @NotNull Class<? extends V> clazz, @NotNull B data) throws Exception;
|
|
||||||
|
|
||||||
public boolean isAdaptedFrom(Class<?> clazz) {
|
public boolean isAdaptedFrom(Class<?> clazz) {
|
||||||
return clazz.isAssignableFrom(valueType);
|
return clazz.isAssignableFrom(valueType);
|
||||||
}
|
}
|
||||||
@@ -41,7 +37,7 @@ public abstract class ValueAdapter<P extends ConfigurationProvider, B, V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAdapterOf(Class<?> clazz) {
|
public boolean isAdapterOf(Class<?> clazz) {
|
||||||
return valueType.isAssignableFrom(clazz);
|
return clazz == valueType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|||||||
@@ -3,20 +3,17 @@ package cc.carm.lib.configuration.adapter;
|
|||||||
import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapters;
|
import cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapters;
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||||
import cc.carm.lib.configuration.source.ConfigurationProvider;
|
import cc.carm.lib.configuration.source.ConfigurationProvider;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ValueAdapterRegistry<P extends ConfigurationProvider> {
|
public class ValueAdapterRegistry<P extends ConfigurationProvider> {
|
||||||
|
|
||||||
protected final @NotNull P provider;
|
|
||||||
protected final Map<Class<?>, ValueAdapter<P, ?, ?>> adapters = new HashMap<>();
|
protected final Map<Class<?>, ValueAdapter<P, ?, ?>> adapters = new HashMap<>();
|
||||||
|
|
||||||
public ValueAdapterRegistry(@NotNull P provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void register(@NotNull ValueAdapter<P, ?, ?> adapter) {
|
public void register(@NotNull ValueAdapter<P, ?, ?> adapter) {
|
||||||
adapters.put(adapter.getValueClass(), adapter);
|
adapters.put(adapter.getValueClass(), adapter);
|
||||||
}
|
}
|
||||||
@@ -46,30 +43,31 @@ public class ValueAdapterRegistry<P extends ConfigurationProvider> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> T deserialize(Class<T> type, Object value) throws Exception {
|
@Contract("_,_,null -> null")
|
||||||
if (value == null) return null;
|
public <T> T deserialize(@NotNull P provider, @NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||||
if (type == Object.class) return type.cast(value);
|
if (source == null) return null; // Null check
|
||||||
|
if (type.isInstance(source)) return type.cast(source); // Not required to deserialize
|
||||||
|
|
||||||
ValueAdapter<P, ?, ?> adapter = getAdapter(type);
|
ValueAdapter<P, ?, ?> adapter = getAdapter(type);
|
||||||
if (adapter == null) throw new RuntimeException("No adapter for type " + type.getName());
|
if (adapter == null) throw new RuntimeException("No adapter for type " + type.getName());
|
||||||
|
|
||||||
// CHECK IF VALUE IS ADAPTED FROM GIVEN VALUE'S TYPE
|
// Check if value is adapted from given value's type
|
||||||
if (adapter.isAdaptedFrom(value)) {
|
if (adapter.isAdaptedFrom(source)) {
|
||||||
return (T) adapter.deserializeObject(provider, type, value);
|
return (T) adapter.deserializeObject(provider, type, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
// OTHERWISE, WE NEED TO DESERIALIZE ONE BY ONE
|
// Otherwise, we need to deserialize one by one.
|
||||||
Object baseValue = deserialize(adapter.getBaseClass(), value);
|
Object baseValue = deserialize(provider, adapter.getBaseClass(), source);
|
||||||
if (baseValue == null) return null; // Null check
|
if (baseValue == null) return null; // Null check
|
||||||
|
|
||||||
return (T) adapter.deserializeObject(provider, type, baseValue);
|
return (T) adapter.deserializeObject(provider, type, baseValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> Object serialize(T value) throws Exception {
|
@Contract("_,null -> null")
|
||||||
if (value == null) return null;
|
public <T> Object serialize(@NotNull P provider, @Nullable T value) throws Exception {
|
||||||
|
if (value == null) return null; // Null check
|
||||||
|
|
||||||
Class<?> valueClass = value.getClass();
|
ValueAdapter<P, ?, ?> adapter = getAdapter(value.getClass());
|
||||||
ValueAdapter<P, ?, ?> adapter = adapters.get(valueClass);
|
|
||||||
if (adapter == null) return value; // No adapters, try to return the original value
|
if (adapter == null) return value; // No adapters, try to return the original value
|
||||||
|
|
||||||
if (adapter instanceof PrimitiveAdapters) {
|
if (adapter instanceof PrimitiveAdapters) {
|
||||||
@@ -79,12 +77,14 @@ public class ValueAdapterRegistry<P extends ConfigurationProvider> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, we need to serialize one by one.
|
// Otherwise, we need to serialize one by one.
|
||||||
return serialize(adapter.serializeObject(provider, value));
|
return serialize(provider, adapter.serializeObject(provider, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueAdapter<P, ?, ?> getAdapter(Class<?> clazz) {
|
public ValueAdapter<P, ?, ?> getAdapter(Class<?> clazz) {
|
||||||
ValueAdapter<P, ?, ?> byClass = adapters.get(clazz);
|
return adapters.getOrDefault(clazz, findAdapter(clazz));
|
||||||
if (byClass != null) return byClass;
|
}
|
||||||
|
|
||||||
|
public ValueAdapter<P, ?, ?> findAdapter(Class<?> clazz) {
|
||||||
return adapters.values().stream().filter(adapter -> adapter.isAdapterOf(clazz)).findFirst().orElse(null);
|
return adapters.values().stream().filter(adapter -> adapter.isAdapterOf(clazz)).findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationProvider;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value deserializer, convert base data to target value.
|
||||||
|
*
|
||||||
|
* @param <P> Configuration provider
|
||||||
|
* @param <B> The type of base data
|
||||||
|
* @param <V> The type of target value
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueDeserializer<P extends ConfigurationProvider, B, V> {
|
||||||
|
|
||||||
|
V deserialize(@NotNull P provider, @NotNull Class<? extends V> clazz, @NotNull B data) throws Exception;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationProvider;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value serializer, convert target value to base data.
|
||||||
|
*
|
||||||
|
* @param <P> Configuration provider
|
||||||
|
* @param <B> The type of base data
|
||||||
|
* @param <V> The type of value
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueSerializer<P extends ConfigurationProvider, B, V> {
|
||||||
|
|
||||||
|
B serialize(@NotNull P provider, @NotNull V value) throws Exception;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,4 +21,9 @@ public class EnumAdapter<P extends ConfigurationProvider> extends ValueAdapter<P
|
|||||||
return Enum.valueOf(clazz, data);
|
return Enum.valueOf(clazz, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAdapterOf(Class<?> clazz) {
|
||||||
|
return clazz.isEnum();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package cc.carm.lib.configuration.annotation;
|
package cc.carm.lib.configuration.annotation;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
@@ -16,7 +14,7 @@ public @interface ConfigPath {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The path value of the current configuration.
|
* The path value of the current configuration.
|
||||||
* If not set,will generate the path by {@link ConfigInitializer#getPathFromName(String)}.
|
* If not set,will generate the path by {@link cc.carm.lib.configuration.source.path.PathGenerator}.
|
||||||
*
|
*
|
||||||
* @return The path value of the current configuration
|
* @return The path value of the current configuration
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,275 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.annotation.ConfigPath;
|
|
||||||
import cc.carm.lib.configuration.annotation.HeaderComment;
|
|
||||||
import cc.carm.lib.configuration.annotation.InlineComment;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
|
||||||
import cc.carm.lib.configuration.value.ConfigValue;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置文件类初始化方法
|
|
||||||
* 用于初始化 {@link Configuration} 中的每个 {@link ConfigValue} 对象
|
|
||||||
*
|
|
||||||
* @param <T> {@link ConfigurationProvider} 配置文件的数据来源
|
|
||||||
* @author CarmJos
|
|
||||||
*/
|
|
||||||
public class ConfigInitializer<T extends ConfigurationProvider<?>> {
|
|
||||||
|
|
||||||
protected final @NotNull T provider;
|
|
||||||
|
|
||||||
public ConfigInitializer(@NotNull T provider) {
|
|
||||||
this.provider = provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param clazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Class<? extends Configuration> clazz, boolean saveDefaults) {
|
|
||||||
initialize(clazz, saveDefaults, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param clazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
* @param loadSubClasses 是否加载内部子类(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Class<? extends Configuration> clazz,
|
|
||||||
boolean saveDefaults, boolean loadSubClasses) {
|
|
||||||
initializeStaticClass(
|
|
||||||
clazz, null, null,
|
|
||||||
null, null, null,
|
|
||||||
saveDefaults, loadSubClasses
|
|
||||||
);
|
|
||||||
if (saveDefaults) {
|
|
||||||
try {
|
|
||||||
provider.save();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration config) {
|
|
||||||
initialize(config, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration config, boolean saveDefaults) {
|
|
||||||
initializeInstance(
|
|
||||||
config, null, null,
|
|
||||||
null, null, null,
|
|
||||||
saveDefaults
|
|
||||||
);
|
|
||||||
if (saveDefaults) {
|
|
||||||
try {
|
|
||||||
provider.save();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 针对实例类的初始化方法
|
|
||||||
private void initializeInstance(@NotNull Configuration root,
|
|
||||||
@Nullable String parentPath, @Nullable String fieldName,
|
|
||||||
@Nullable ConfigPath fieldPath,
|
|
||||||
@Nullable HeaderComment fieldHeaderComments,
|
|
||||||
@Nullable InlineComment fieldInlineComments,
|
|
||||||
boolean saveDefaults) {
|
|
||||||
String path = getClassPath(root.getClass(), parentPath, fieldName, fieldPath);
|
|
||||||
this.provider.setHeaderComment(path, getClassHeaderComments(root.getClass(), fieldHeaderComments));
|
|
||||||
if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments));
|
|
||||||
|
|
||||||
for (Field field : root.getClass().getDeclaredFields()) {
|
|
||||||
initializeField(root, field, path, saveDefaults, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 针对静态类的初始化方法
|
|
||||||
private void initializeStaticClass(@NotNull Class<?> clazz,
|
|
||||||
@Nullable String parentPath, @Nullable String fieldName,
|
|
||||||
@Nullable ConfigPath fieldPath,
|
|
||||||
@Nullable HeaderComment fieldHeaderComments,
|
|
||||||
@Nullable InlineComment fieldInlineComments,
|
|
||||||
boolean saveDefaults, boolean loadSubClasses) {
|
|
||||||
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));
|
|
||||||
|
|
||||||
for (Field field : clazz.getDeclaredFields()) {
|
|
||||||
initializeField(clazz, field, path, saveDefaults, loadSubClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!loadSubClasses) return;
|
|
||||||
Class<?>[] classes = clazz.getDeclaredClasses();
|
|
||||||
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
|
|
||||||
initializeStaticClass(
|
|
||||||
classes[i], path, classes[i].getSimpleName(),
|
|
||||||
null, null, null,
|
|
||||||
saveDefaults, true
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeField(@NotNull Object source, @NotNull Field field,
|
|
||||||
@Nullable String parent, boolean saveDefaults, boolean loadSubClasses) {
|
|
||||||
try {
|
|
||||||
field.setAccessible(true);
|
|
||||||
Object object = field.get(source);
|
|
||||||
|
|
||||||
if (object instanceof ConfigValue<?>) {
|
|
||||||
initializeValue(
|
|
||||||
(ConfigValue<?>) object, getFieldPath(field, parent),
|
|
||||||
field.getAnnotation(HeaderComment.class),
|
|
||||||
field.getAnnotation(InlineComment.class),
|
|
||||||
saveDefaults
|
|
||||||
);
|
|
||||||
} else if (source instanceof Configuration && object instanceof Configuration) {
|
|
||||||
// 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。
|
|
||||||
initializeInstance(
|
|
||||||
(Configuration) object, parent, field.getName(),
|
|
||||||
field.getAnnotation(ConfigPath.class),
|
|
||||||
field.getAnnotation(HeaderComment.class),
|
|
||||||
field.getAnnotation(InlineComment.class),
|
|
||||||
saveDefaults
|
|
||||||
);
|
|
||||||
} else if (source instanceof Class<?> && object instanceof Class<?>) {
|
|
||||||
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
|
|
||||||
initializeStaticClass(
|
|
||||||
(Class<?>) object, parent, field.getName(),
|
|
||||||
field.getAnnotation(ConfigPath.class),
|
|
||||||
field.getAnnotation(HeaderComment.class),
|
|
||||||
field.getAnnotation(InlineComment.class),
|
|
||||||
saveDefaults, loadSubClasses
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 以上判断实现以下规范:
|
|
||||||
// - 实例类中仅加载 ConfigValue实例 与 ConfigurationRoot实例
|
|
||||||
// - 静态类中仅加载 静态ConfigValue实例 与 静态ConfigurationRoot类
|
|
||||||
|
|
||||||
} catch (IllegalAccessException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initializeValue(@NotNull ConfigValue<?> value, @NotNull String path,
|
|
||||||
@Nullable HeaderComment fieldHeaderComment,
|
|
||||||
@Nullable InlineComment fieldInlineComment,
|
|
||||||
boolean saveDefaults) {
|
|
||||||
value.initialize(
|
|
||||||
provider, saveDefaults, path,
|
|
||||||
readHeaderComments(fieldHeaderComment),
|
|
||||||
readInlineComments(fieldInlineComment)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static @Nullable List<String> getClassHeaderComments(@NotNull Class<?> clazz,
|
|
||||||
@Nullable HeaderComment fieldAnnotation) {
|
|
||||||
List<String> classComments = readHeaderComments(clazz.getAnnotation(HeaderComment.class));
|
|
||||||
if (classComments != null) return classComments;
|
|
||||||
else return readHeaderComments(fieldAnnotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected static List<String> readHeaderComments(@Nullable HeaderComment annotation) {
|
|
||||||
if (annotation == null) return null;
|
|
||||||
String[] value = annotation.value();
|
|
||||||
return value.length > 0 ? Arrays.asList(value) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected static @Nullable String readInlineComments(@Nullable InlineComment annotation) {
|
|
||||||
if (annotation == null) return null;
|
|
||||||
String value = annotation.value();
|
|
||||||
return value.length() > 0 ? value : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static @Nullable String getClassPath(@Nullable Class<?> clazz,
|
|
||||||
@Nullable String parentPath,
|
|
||||||
@Nullable String filedName,
|
|
||||||
@Nullable ConfigPath fieldAnnotation) {
|
|
||||||
@NotNull String parent = parentPath != null ? parentPath + "." : "";
|
|
||||||
boolean fromRoot = false;
|
|
||||||
|
|
||||||
// 先获取 Class 对应的路径注解 其优先度最高。
|
|
||||||
if (clazz != null) {
|
|
||||||
ConfigPath clazzAnnotation = clazz.getAnnotation(ConfigPath.class);
|
|
||||||
if (clazzAnnotation != null) {
|
|
||||||
fromRoot = clazzAnnotation.root();
|
|
||||||
if (clazzAnnotation.value().length() > 0) {
|
|
||||||
return (fromRoot ? "" : parent) + clazzAnnotation.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fieldAnnotation != null) {
|
|
||||||
fromRoot = fromRoot || fieldAnnotation.root();
|
|
||||||
if (fieldAnnotation.value().length() > 0) {
|
|
||||||
return (fromRoot ? "" : parent) + fieldAnnotation.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 再由 fieldName 获取路径
|
|
||||||
if (filedName != null) return (fromRoot ? "" : parent) + getPathFromName(filedName);
|
|
||||||
else return null; // 不满足上述条件 且 无 fieldName,则说明是根路径。
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static @NotNull String getFieldPath(@NotNull Field field, @Nullable String parentPath) {
|
|
||||||
@NotNull String parent = parentPath != null ? parentPath + "." : "";
|
|
||||||
boolean fromRoot = false;
|
|
||||||
|
|
||||||
// 先获取 Field 对应的路径注解 其优先度最高。
|
|
||||||
ConfigPath pathAnnotation = field.getAnnotation(ConfigPath.class);
|
|
||||||
if (pathAnnotation != null) {
|
|
||||||
fromRoot = pathAnnotation.root();
|
|
||||||
if (pathAnnotation.value().length() > 0) {
|
|
||||||
return (fromRoot ? "" : parent) + pathAnnotation.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 最后再通过 fieldName 自动生成路径
|
|
||||||
return (fromRoot ? "" : parent) + getPathFromName(field.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 得到指定元素的配置名称。
|
|
||||||
* 采用 全小写,以“-”链接 的命名规则。
|
|
||||||
*
|
|
||||||
* @param name 源名称
|
|
||||||
* @return 全小写,以“-”链接 的 路径名称
|
|
||||||
*/
|
|
||||||
public static String getPathFromName(String name) {
|
|
||||||
return name.replaceAll("[A-Z]", "-$0") // 将驼峰转换为蛇形;
|
|
||||||
.replaceAll("-(.*)", "$1") // 若首字母也为大写,则也会被转换,需要去掉第一个横线
|
|
||||||
.replaceAll("_-([A-Z])", "_$1") // 因为命名中可能包含 _,因此需要被特殊处理一下
|
|
||||||
.replaceAll("([a-z])-([A-Z])", "$1_$2") // 然后将非全大写命名的内容进行转换
|
|
||||||
.replace("-", "") // 移除掉多余的横线
|
|
||||||
.replace("_", "-") // 将下划线替换为横线
|
|
||||||
.toLowerCase(); // 最后转为全小写
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -5,4 +5,5 @@ package cc.carm.lib.configuration.core;
|
|||||||
* which is used to label and record the configuration information.
|
* which is used to label and record the configuration information.
|
||||||
*/
|
*/
|
||||||
public interface Configuration {
|
public interface Configuration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package cc.carm.lib.configuration.core.source;
|
package cc.carm.lib.configuration.core.source;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
|
||||||
import cc.carm.lib.configuration.core.Configuration;
|
import cc.carm.lib.configuration.core.Configuration;
|
||||||
|
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||||
import cc.carm.lib.configuration.value.ConfigValue;
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package cc.carm.lib.configuration.manifest;
|
||||||
|
|
||||||
|
public class ValueManifest {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,15 +1,85 @@
|
|||||||
package cc.carm.lib.configuration.source;
|
package cc.carm.lib.configuration.source;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
|
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
|
||||||
|
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||||
|
import cc.carm.lib.configuration.source.path.PathGenerator;
|
||||||
|
import cc.carm.lib.easyoptions.OptionHolder;
|
||||||
|
import cc.carm.lib.easyoptions.OptionType;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public abstract class ConfigurationBuilder<P extends ConfigurationProvider> {
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
protected ConfigurationLoader loader;
|
public abstract class ConfigurationBuilder<P extends ConfigurationProvider<P>, C> {
|
||||||
protected ValueAdapterRegistry<P> processors;
|
|
||||||
|
|
||||||
|
|
||||||
|
protected Function<P, ConfigurationLoader<P>> loaderFunction = ConfigurationLoader::new;
|
||||||
|
protected Consumer<ConfigurationLoader<P>> loaderConsumer = loader -> {
|
||||||
|
};
|
||||||
|
|
||||||
public abstract @NotNull ConfigurationProvider build();
|
protected ValueAdapterRegistry<P> adapters = new ValueAdapterRegistry<>();
|
||||||
|
protected OptionHolder options = new OptionHolder();
|
||||||
|
|
||||||
|
public abstract C getThis();
|
||||||
|
|
||||||
|
public C loader(Function<P, ConfigurationLoader<P>> loaderFunction) {
|
||||||
|
this.loaderFunction = loaderFunction;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public C loader(ConfigurationLoader<P> loader) {
|
||||||
|
return loader(p -> loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public C loader(Consumer<ConfigurationLoader<P>> loaderConsumer) {
|
||||||
|
this.loaderConsumer = this.loaderConsumer.andThen(loaderConsumer);
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public C pathGenerator(PathGenerator<P> pathGenerator) {
|
||||||
|
return loader(loader -> {
|
||||||
|
loader.setPathGenerator(pathGenerator);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public C adapters(ValueAdapterRegistry<P> adapters) {
|
||||||
|
this.adapters = adapters;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public C adapter(Consumer<ValueAdapterRegistry<P>> adapterRegistryConsumer) {
|
||||||
|
adapterRegistryConsumer.accept(adapters);
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public C adapter(@NotNull ValueAdapter<P, ?, ?> adapter) {
|
||||||
|
return adapter(a -> a.register(adapter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> C adapter(Class<T> clazz, @NotNull ValueAdapter<P, ?, T> adapter) {
|
||||||
|
return adapter(a -> a.register(clazz, adapter));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <B, V> C adapter(Class<B> baseClass, Class<V> valueClass,
|
||||||
|
ConfigDataFunction<B, V> parser, ConfigDataFunction<V, B> serializer) {
|
||||||
|
return adapter(a -> a.register(baseClass, valueClass, parser, serializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public C options(OptionHolder options) {
|
||||||
|
this.options = options;
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public C option(Consumer<OptionHolder> optionsConsumer) {
|
||||||
|
optionsConsumer.accept(options);
|
||||||
|
return getThis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <O> C option(OptionType<O> option, O value) {
|
||||||
|
return option(o -> o.set(option, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract @NotNull P build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,196 @@
|
|||||||
package cc.carm.lib.configuration.source;
|
package cc.carm.lib.configuration.source;
|
||||||
|
|
||||||
public interface ConfigurationLoader {
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
|
import cc.carm.lib.configuration.core.Configuration;
|
||||||
|
import cc.carm.lib.configuration.source.path.PathGenerator;
|
||||||
|
import cc.carm.lib.configuration.source.path.StandardPathGenerator;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration loader,
|
||||||
|
* used to load configuration values from {@link cc.carm.lib.configuration.core.Configuration} classes.
|
||||||
|
*/
|
||||||
|
public class ConfigurationLoader<P extends ConfigurationProvider<P>> {
|
||||||
|
|
||||||
|
protected final P provider;
|
||||||
|
protected PathGenerator<P> pathGenerator;
|
||||||
|
|
||||||
|
public ConfigurationLoader(P provider) {
|
||||||
|
this(provider, StandardPathGenerator.of(provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationLoader(P provider, PathGenerator<P> pathGenerator) {
|
||||||
|
this.provider = provider;
|
||||||
|
this.pathGenerator = pathGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPathGenerator(PathGenerator<P> pathGenerator) {
|
||||||
|
this.pathGenerator = pathGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathGenerator<P> getPathGenerator() {
|
||||||
|
return pathGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
||||||
|
*
|
||||||
|
* @param clazz 配置文件类,须继承于 {@link Configuration} 。
|
||||||
|
*/
|
||||||
|
public void initialize(@NotNull P provider, @NotNull Class<? extends Configuration> clazz) {
|
||||||
|
initialize(clazz, saveDefaults, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化指定类的所有 {@link ConfigValue} 对象。
|
||||||
|
*
|
||||||
|
* @param clazz 配置文件类,须继承于 {@link Configuration} 。
|
||||||
|
* @param saveDefaults 是否写入默认值(默认为 true)。
|
||||||
|
* @param loadSubClasses 是否加载内部子类(默认为 true)。
|
||||||
|
*/
|
||||||
|
public void initialize(@NotNull Class<? extends Configuration> clazz) {
|
||||||
|
initializeStaticClass(
|
||||||
|
clazz, null, null,
|
||||||
|
null, null, null,
|
||||||
|
saveDefaults, loadSubClasses
|
||||||
|
);
|
||||||
|
if (saveDefaults) {
|
||||||
|
try {
|
||||||
|
provider.save();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
||||||
|
*
|
||||||
|
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
||||||
|
*/
|
||||||
|
public void initialize(@NotNull Configuration config) {
|
||||||
|
initialize(config, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
||||||
|
*
|
||||||
|
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
||||||
|
* @param saveDefaults 是否写入默认值(默认为 true)。
|
||||||
|
*/
|
||||||
|
public void initialize(@NotNull Configuration config, boolean saveDefaults) {
|
||||||
|
initializeInstance(
|
||||||
|
config, null, null,
|
||||||
|
null, null, null,
|
||||||
|
saveDefaults
|
||||||
|
);
|
||||||
|
if (saveDefaults) {
|
||||||
|
try {
|
||||||
|
provider.save();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 针对实例类的初始化方法
|
||||||
|
private void initializeInstance(@NotNull Configuration root,
|
||||||
|
@Nullable String parentPath, @Nullable String fieldName,
|
||||||
|
@Nullable ConfigPath fieldPath,
|
||||||
|
@Nullable HeaderComment fieldHeaderComments,
|
||||||
|
@Nullable InlineComment fieldInlineComments,
|
||||||
|
boolean saveDefaults) {
|
||||||
|
String path = getClassPath(root.getClass(), parentPath, fieldName, fieldPath);
|
||||||
|
this.provider.setHeaderComment(path, getClassHeaderComments(root.getClass(), fieldHeaderComments));
|
||||||
|
if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments));
|
||||||
|
|
||||||
|
for (Field field : root.getClass().getDeclaredFields()) {
|
||||||
|
initializeField(root, field, path, saveDefaults, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 针对静态类的初始化方法
|
||||||
|
private void initializeStaticClass(@NotNull Class<?> clazz,
|
||||||
|
@Nullable String parentPath, @Nullable String fieldName,
|
||||||
|
@Nullable ConfigPath fieldPath,
|
||||||
|
@Nullable HeaderComment fieldHeaderComments,
|
||||||
|
@Nullable InlineComment fieldInlineComments,
|
||||||
|
boolean saveDefaults, boolean loadSubClasses) {
|
||||||
|
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));
|
||||||
|
|
||||||
|
for (Field field : clazz.getDeclaredFields()) {
|
||||||
|
initializeField(clazz, field, path, saveDefaults, loadSubClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loadSubClasses) return;
|
||||||
|
Class<?>[] classes = clazz.getDeclaredClasses();
|
||||||
|
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
|
||||||
|
initializeStaticClass(
|
||||||
|
classes[i], path, classes[i].getSimpleName(),
|
||||||
|
null, null, null,
|
||||||
|
saveDefaults, true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeField(@NotNull Object source, @NotNull Field field,
|
||||||
|
@Nullable String parent, boolean saveDefaults, boolean loadSubClasses) {
|
||||||
|
try {
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object object = field.get(source);
|
||||||
|
|
||||||
|
if (object instanceof ConfigValue<?>) {
|
||||||
|
initializeValue(
|
||||||
|
(ConfigValue<?>) object, getFieldPath(field, parent),
|
||||||
|
field.getAnnotation(HeaderComment.class),
|
||||||
|
field.getAnnotation(InlineComment.class),
|
||||||
|
saveDefaults
|
||||||
|
);
|
||||||
|
} else if (source instanceof Configuration && object instanceof Configuration) {
|
||||||
|
// 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。
|
||||||
|
initializeInstance(
|
||||||
|
(Configuration) object, parent, field.getName(),
|
||||||
|
field.getAnnotation(ConfigPath.class),
|
||||||
|
field.getAnnotation(HeaderComment.class),
|
||||||
|
field.getAnnotation(InlineComment.class),
|
||||||
|
saveDefaults
|
||||||
|
);
|
||||||
|
} else if (source instanceof Class<?> && object instanceof Class<?>) {
|
||||||
|
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
|
||||||
|
initializeStaticClass(
|
||||||
|
(Class<?>) object, parent, field.getName(),
|
||||||
|
field.getAnnotation(ConfigPath.class),
|
||||||
|
field.getAnnotation(HeaderComment.class),
|
||||||
|
field.getAnnotation(InlineComment.class),
|
||||||
|
saveDefaults, loadSubClasses
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 以上判断实现以下规范:
|
||||||
|
// - 实例类中仅加载 ConfigValue实例 与 ConfigurationRoot实例
|
||||||
|
// - 静态类中仅加载 静态ConfigValue实例 与 静态ConfigurationRoot类
|
||||||
|
|
||||||
|
} catch (IllegalAccessException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void initializeValue(@NotNull ConfigValue<?> value, @NotNull String path,
|
||||||
|
@Nullable HeaderComment fieldHeaderComment,
|
||||||
|
@Nullable InlineComment fieldInlineComment,
|
||||||
|
boolean saveDefaults) {
|
||||||
|
value.initialize(
|
||||||
|
provider, saveDefaults, path,
|
||||||
|
readHeaderComments(fieldHeaderComment),
|
||||||
|
readInlineComments(fieldInlineComment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,37 @@
|
|||||||
package cc.carm.lib.configuration.source;
|
package cc.carm.lib.configuration.source;
|
||||||
|
|
||||||
public class ConfigurationProvider {
|
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
|
||||||
|
import cc.carm.lib.configuration.core.Configuration;
|
||||||
|
import cc.carm.lib.easyoptions.OptionHolder;
|
||||||
|
import cc.carm.lib.easyoptions.OptionType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public abstract class ConfigurationProvider<P extends ConfigurationProvider<P>> {
|
||||||
|
|
||||||
|
protected @NotNull ConfigurationLoader<P> loader = new ConfigurationLoader<>();
|
||||||
|
protected @NotNull ValueAdapterRegistry<P> adapters = new ValueAdapterRegistry<>();
|
||||||
|
protected @NotNull OptionHolder options = new OptionHolder();
|
||||||
|
|
||||||
|
|
||||||
|
public OptionHolder options() {
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> @NotNull T option(@NotNull OptionType<T> option) {
|
||||||
|
return options.get(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void option(@NotNull OptionType<T> option, @NotNull T value) {
|
||||||
|
options.set(option, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationLoader<P> loader() {
|
||||||
|
return loader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(Configuration configuration) {
|
||||||
|
loader().load(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package cc.carm.lib.configuration.source.path;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationProvider;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public interface PathGenerator<P extends ConfigurationProvider<P>> {
|
||||||
|
|
||||||
|
@Nullable String getFieldPath(@Nullable String parentPath, @NotNull Field field);
|
||||||
|
|
||||||
|
@Nullable String getClassPath(@Nullable String parentPath,
|
||||||
|
@NotNull Class<?> clazz, @Nullable Field clazzField);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the configuration name of the specified element.
|
||||||
|
* Use the naming convention of all lowercase and "-" links.
|
||||||
|
*
|
||||||
|
* @param name source name
|
||||||
|
* @return the final path
|
||||||
|
*/
|
||||||
|
static String covertPathName(String name) {
|
||||||
|
return name.replaceAll("[A-Z]", "-$0") // 将驼峰形转换为蛇形;
|
||||||
|
.replaceAll("-(.*)", "$1") // 若首字母也为大写,则也会被转换,需要去掉第一个横线
|
||||||
|
.replaceAll("_-([A-Z])", "_$1") // 因为命名中可能包含 _,因此需要被特殊处理一下
|
||||||
|
.replaceAll("([a-z])-([A-Z])", "$1_$2") // 然后将非全大写命名的内容进行转换
|
||||||
|
.replace("-", "") // 移除掉多余的横线
|
||||||
|
.replace("_", "-") // 将下划线替换为横线
|
||||||
|
.toLowerCase(); // 最后转为全小写
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package cc.carm.lib.configuration.source.path;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationProvider;
|
||||||
|
import cc.carm.lib.configuration.source.standard.ConfigurationOptions;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
public class StandardPathGenerator<P extends ConfigurationProvider<P>> implements PathGenerator<P> {
|
||||||
|
|
||||||
|
public static <T extends ConfigurationProvider<T>> StandardPathGenerator<T> of(T provider) {
|
||||||
|
return of(provider, PathGenerator::covertPathName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends ConfigurationProvider<T>> StandardPathGenerator<T> of(T provider, UnaryOperator<String> pathConverter) {
|
||||||
|
return new StandardPathGenerator<>(provider, pathConverter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final P provider;
|
||||||
|
protected UnaryOperator<String> pathConverter;
|
||||||
|
|
||||||
|
public StandardPathGenerator(P provider, UnaryOperator<String> pathConverter) {
|
||||||
|
this.provider = provider;
|
||||||
|
this.pathConverter = pathConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull UnaryOperator<String> getPathConverter() {
|
||||||
|
return pathConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPathConverter(UnaryOperator<String> pathConverter) {
|
||||||
|
this.pathConverter = pathConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String covertPath(String name) {
|
||||||
|
return pathConverter.apply(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public char pathSeparator() {
|
||||||
|
return provider.option(ConfigurationOptions.PATH_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String link(@Nullable String parent, boolean root, @Nullable String path) {
|
||||||
|
if (path == null || path.isEmpty()) return root ? null : parent;
|
||||||
|
return root && parent != null ? covertPath(path) : parent + pathSeparator() + covertPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getFieldPath(@Nullable String parentPath, @NotNull Field field) {
|
||||||
|
ConfigPath path = field.getAnnotation(ConfigPath.class);
|
||||||
|
if (path == null) return link(parentPath, false, field.getName()); // No annotation, use field name.
|
||||||
|
else return link(parentPath, path.root(), path.value().isEmpty() ? field.getName() : path.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getClassPath(@Nullable String parentPath, @NotNull Class<?> clazz, @Nullable Field clazzField) {
|
||||||
|
// For standard path generator, we generate path following by:
|
||||||
|
// 1. Check if the class has a ConfigPath annotation, if so, use the root and value as the path.
|
||||||
|
// 2. If the class defined as a field, check if the field has a ConfigPath annotation,
|
||||||
|
// and use filed information.
|
||||||
|
ConfigPath clazzPath = clazz.getAnnotation(ConfigPath.class);
|
||||||
|
|
||||||
|
if (clazzPath != null) return link(parentPath, clazzPath.root(), clazzPath.value());
|
||||||
|
if (clazzField == null) return parentPath; // No field, return same as parent.
|
||||||
|
|
||||||
|
ConfigPath fieldPath = clazzField.getAnnotation(ConfigPath.class);
|
||||||
|
if (fieldPath == null) return link(parentPath, false, clazzField.getName());
|
||||||
|
else return getFieldPath(parentPath, clazzField);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package cc.carm.lib.configuration.source.standard;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
|
import cc.carm.lib.easyannotation.AnnotatedMetaType;
|
||||||
|
|
||||||
|
public interface ConfigurationMetaTypes {
|
||||||
|
|
||||||
|
AnnotatedMetaType<ConfigPath, String> PATH = AnnotatedMetaType.of(ConfigPath.class, ConfigPath::value);
|
||||||
|
|
||||||
|
AnnotatedMetaType<ConfigPath, Boolean> ROOT = AnnotatedMetaType.of(ConfigPath.class, ConfigPath::root);
|
||||||
|
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package cc.carm.lib.configuration.source.standard;
|
||||||
|
|
||||||
|
|
||||||
|
import cc.carm.lib.easyoptions.OptionType;
|
||||||
|
|
||||||
|
import static cc.carm.lib.easyoptions.OptionType.of;
|
||||||
|
|
||||||
|
public interface ConfigurationOptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration path separator.
|
||||||
|
*/
|
||||||
|
OptionType<Character> PATH_SEPARATOR = of('.');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to copy files from resource if exists.
|
||||||
|
*/
|
||||||
|
OptionType<Boolean> COPY_DEFAULTS = of(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to save default values if offered and not exists in configuration.
|
||||||
|
*/
|
||||||
|
OptionType<Boolean> SAVE_DEFAULTS = of(true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to load subclasses of configuration class.
|
||||||
|
*/
|
||||||
|
OptionType<Boolean> LOAD_SUB_CLASSES = of(true);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
package cc.carm.lib.configuration.value;
|
package cc.carm.lib.configuration.value;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public class AdaptTest {
|
|||||||
@Test
|
@Test
|
||||||
public void test() throws Exception {
|
public void test() throws Exception {
|
||||||
|
|
||||||
ValueAdapterRegistry<?> registry = new ValueAdapterRegistry<>(new ConfigurationProvider());
|
ValueAdapterRegistry<ConfigurationProvider> registry = new ValueAdapterRegistry<>();
|
||||||
registry.register(Long.class, PrimitiveAdapters.ofLong());
|
registry.register(Long.class, PrimitiveAdapters.ofLong());
|
||||||
registry.register(long.class, PrimitiveAdapters.ofLong());
|
registry.register(long.class, PrimitiveAdapters.ofLong());
|
||||||
registry.register(Integer.class, PrimitiveAdapters.ofInteger());
|
registry.register(Integer.class, PrimitiveAdapters.ofInteger());
|
||||||
@@ -36,16 +36,19 @@ public class AdaptTest {
|
|||||||
registry.register(
|
registry.register(
|
||||||
Duration.class, LocalTime.class,
|
Duration.class, LocalTime.class,
|
||||||
duration -> LocalTime.now().plus(duration),
|
duration -> LocalTime.now().plus(duration),
|
||||||
data -> Duration.between(data, LocalTime.now())
|
data -> Duration.between(LocalTime.now(), data)
|
||||||
);
|
);
|
||||||
|
|
||||||
LocalTime v = registry.deserialize(LocalTime.class, "600");
|
ConfigurationProvider provider = new ConfigurationProvider();
|
||||||
Object d = registry.serialize(v);
|
|
||||||
|
|
||||||
|
LocalTime v = registry.deserialize(provider, LocalTime.class, "600");
|
||||||
|
Object d = registry.serialize(provider, v);
|
||||||
|
|
||||||
System.out.println(v);
|
System.out.println(v);
|
||||||
System.out.println(d);
|
System.out.println(d);
|
||||||
System.out.println(registry.deserialize(TestEnum.class, "b"));
|
System.out.println(registry.deserialize(provider, TestEnum.class, "b"));
|
||||||
|
System.out.println(registry.serialize(provider, TestEnum.C).getClass());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TestEnum {
|
enum TestEnum {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class NameTest {
|
public class NameTest {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package cc.carm.lib.configuration.demo.tests.conf;
|
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.Configuration;
|
||||||
import cc.carm.lib.configuration.annotation.ConfigPath;
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
import cc.carm.lib.configuration.annotation.HeaderComment;
|
import cc.carm.lib.configuration.annotation.HeaderComment;
|
||||||
|
|||||||
+1
-2
@@ -1,7 +1,6 @@
|
|||||||
package cc.carm.lib.configuration.hocon;
|
package cc.carm.lib.configuration.hocon;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
|
||||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||||
import cc.carm.lib.configuration.hocon.exception.HOCONGetValueException;
|
import cc.carm.lib.configuration.hocon.exception.HOCONGetValueException;
|
||||||
import cc.carm.lib.configuration.hocon.util.HOCONUtils;
|
import cc.carm.lib.configuration.hocon.util.HOCONUtils;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package cc.carm.lib.configuration.json;
|
package cc.carm.lib.configuration.json;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package cc.carm.lib.configuration.sql;
|
package cc.carm.lib.configuration.sql;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||||
import cc.carm.lib.easysql.api.SQLManager;
|
import cc.carm.lib.easysql.api.SQLManager;
|
||||||
import cc.carm.lib.easysql.api.SQLQuery;
|
import cc.carm.lib.easysql.api.SQLQuery;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package cc.carm.lib.configuration.yaml;
|
package cc.carm.lib.configuration.yaml;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
|
||||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||||
import cc.carm.lib.yamlcommentupdater.CommentedYAML;
|
import cc.carm.lib.yamlcommentupdater.CommentedYAML;
|
||||||
import cc.carm.lib.yamlcommentupdater.CommentedYAMLWriter;
|
import cc.carm.lib.yamlcommentupdater.CommentedYAMLWriter;
|
||||||
@@ -26,6 +25,7 @@ public class YAMLConfigProvider extends FileConfigProvider<YAMLSectionWrapper> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void initializeConfig() {
|
public void initializeConfig() {
|
||||||
|
ConfigurationOptions
|
||||||
this.configuration = YamlConfiguration.loadConfiguration(file);
|
this.configuration = YamlConfiguration.loadConfiguration(file);
|
||||||
this.initializer = new ConfigInitializer<>(this);
|
this.initializer = new ConfigInitializer<>(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
<module>impl/json</module>
|
<module>impl/json</module>
|
||||||
<module>impl/sql</module>
|
<module>impl/sql</module>
|
||||||
<module>impl/hocon</module>
|
<module>impl/hocon</module>
|
||||||
|
<module>commentable</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<name>EasyConfiguration</name>
|
<name>EasyConfiguration</name>
|
||||||
|
|||||||
Reference in New Issue
Block a user