mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 18:48: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.Nullable;
|
||||
@@ -20,7 +20,6 @@ public class ConfigurationComments {
|
||||
}
|
||||
|
||||
public void setHeaderComments(@Nullable String path, @Nullable List<String> comments) {
|
||||
|
||||
if (comments == null) {
|
||||
getHeaderComments().remove(path);
|
||||
} else {
|
||||
@@ -28,7 +27,6 @@ public class ConfigurationComments {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setInlineComment(@NotNull String path, @Nullable String comment) {
|
||||
if (comment == null) {
|
||||
getInlineComments().remove(path);
|
||||
@@ -47,5 +45,4 @@ public class ConfigurationComments {
|
||||
return getInlineComments().get(path);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -18,6 +18,19 @@
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<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>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
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.
|
||||
@@ -10,7 +9,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
* @param <B> The type of the base data
|
||||
* @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 V> valueType;
|
||||
@@ -28,10 +28,6 @@ public abstract class ValueAdapter<P extends ConfigurationProvider, B, V> {
|
||||
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) {
|
||||
return clazz.isAssignableFrom(valueType);
|
||||
}
|
||||
@@ -41,7 +37,7 @@ public abstract class ValueAdapter<P extends ConfigurationProvider, B, V> {
|
||||
}
|
||||
|
||||
public boolean isAdapterOf(Class<?> clazz) {
|
||||
return valueType.isAssignableFrom(clazz);
|
||||
return clazz == valueType;
|
||||
}
|
||||
|
||||
@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.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.source.ConfigurationProvider;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ValueAdapterRegistry<P extends ConfigurationProvider> {
|
||||
|
||||
protected final @NotNull P provider;
|
||||
protected final Map<Class<?>, ValueAdapter<P, ?, ?>> adapters = new HashMap<>();
|
||||
|
||||
public ValueAdapterRegistry(@NotNull P provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public void register(@NotNull ValueAdapter<P, ?, ?> adapter) {
|
||||
adapters.put(adapter.getValueClass(), adapter);
|
||||
}
|
||||
@@ -46,30 +43,31 @@ public class ValueAdapterRegistry<P extends ConfigurationProvider> {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T deserialize(Class<T> type, Object value) throws Exception {
|
||||
if (value == null) return null;
|
||||
if (type == Object.class) return type.cast(value);
|
||||
@Contract("_,_,null -> null")
|
||||
public <T> T deserialize(@NotNull P provider, @NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||
if (source == null) return null; // Null check
|
||||
if (type.isInstance(source)) return type.cast(source); // Not required to deserialize
|
||||
|
||||
ValueAdapter<P, ?, ?> adapter = getAdapter(type);
|
||||
if (adapter == null) throw new RuntimeException("No adapter for type " + type.getName());
|
||||
|
||||
// CHECK IF VALUE IS ADAPTED FROM GIVEN VALUE'S TYPE
|
||||
if (adapter.isAdaptedFrom(value)) {
|
||||
return (T) adapter.deserializeObject(provider, type, value);
|
||||
// Check if value is adapted from given value's type
|
||||
if (adapter.isAdaptedFrom(source)) {
|
||||
return (T) adapter.deserializeObject(provider, type, source);
|
||||
}
|
||||
|
||||
// OTHERWISE, WE NEED TO DESERIALIZE ONE BY ONE
|
||||
Object baseValue = deserialize(adapter.getBaseClass(), value);
|
||||
// Otherwise, we need to deserialize one by one.
|
||||
Object baseValue = deserialize(provider, adapter.getBaseClass(), source);
|
||||
if (baseValue == null) return null; // Null check
|
||||
|
||||
return (T) adapter.deserializeObject(provider, type, baseValue);
|
||||
}
|
||||
|
||||
public <T> Object serialize(T value) throws Exception {
|
||||
if (value == null) return null;
|
||||
@Contract("_,null -> 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 = adapters.get(valueClass);
|
||||
ValueAdapter<P, ?, ?> adapter = getAdapter(value.getClass());
|
||||
if (adapter == null) return value; // No adapters, try to return the original value
|
||||
|
||||
if (adapter instanceof PrimitiveAdapters) {
|
||||
@@ -79,12 +77,14 @@ public class ValueAdapterRegistry<P extends ConfigurationProvider> {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
ValueAdapter<P, ?, ?> byClass = adapters.get(clazz);
|
||||
if (byClass != null) return byClass;
|
||||
return adapters.getOrDefault(clazz, findAdapter(clazz));
|
||||
}
|
||||
|
||||
public ValueAdapter<P, ?, ?> findAdapter(Class<?> clazz) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdapterOf(Class<?> clazz) {
|
||||
return clazz.isEnum();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package cc.carm.lib.configuration.annotation;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -16,7 +14,7 @@ public @interface ConfigPath {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
public interface Configuration {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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.source.comment.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
||||
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;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
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;
|
||||
|
||||
public abstract class ConfigurationBuilder<P extends ConfigurationProvider> {
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
protected ConfigurationLoader loader;
|
||||
protected ValueAdapterRegistry<P> processors;
|
||||
public abstract class ConfigurationBuilder<P extends ConfigurationProvider<P>, C> {
|
||||
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -12,7 +12,7 @@ public class AdaptTest {
|
||||
@Test
|
||||
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(Integer.class, PrimitiveAdapters.ofInteger());
|
||||
@@ -36,16 +36,19 @@ public class AdaptTest {
|
||||
registry.register(
|
||||
Duration.class, LocalTime.class,
|
||||
duration -> LocalTime.now().plus(duration),
|
||||
data -> Duration.between(data, LocalTime.now())
|
||||
data -> Duration.between(LocalTime.now(), data)
|
||||
);
|
||||
|
||||
LocalTime v = registry.deserialize(LocalTime.class, "600");
|
||||
Object d = registry.serialize(v);
|
||||
ConfigurationProvider provider = new ConfigurationProvider();
|
||||
|
||||
LocalTime v = registry.deserialize(provider, LocalTime.class, "600");
|
||||
Object d = registry.serialize(provider, v);
|
||||
|
||||
System.out.println(v);
|
||||
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 {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NameTest {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.annotation.HeaderComment;
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
package cc.carm.lib.configuration.hocon;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||
import cc.carm.lib.configuration.hocon.exception.HOCONGetValueException;
|
||||
import cc.carm.lib.configuration.hocon.util.HOCONUtils;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cc.carm.lib.configuration.json;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cc.carm.lib.configuration.sql;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.easysql.api.SQLManager;
|
||||
import cc.carm.lib.easysql.api.SQLQuery;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package cc.carm.lib.configuration.yaml;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||
import cc.carm.lib.yamlcommentupdater.CommentedYAML;
|
||||
import cc.carm.lib.yamlcommentupdater.CommentedYAMLWriter;
|
||||
@@ -26,6 +25,7 @@ public class YAMLConfigProvider extends FileConfigProvider<YAMLSectionWrapper> i
|
||||
}
|
||||
|
||||
public void initializeConfig() {
|
||||
ConfigurationOptions
|
||||
this.configuration = YamlConfiguration.loadConfiguration(file);
|
||||
this.initializer = new ConfigInitializer<>(this);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user