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

[2.3.0] 版本更新

- [U] 基于 tchristofferson/ConfigUpdater 项目重写YAML相关配置文件的注释部分。
- [A] 为 @ConfigComment 注解添加 ”statWrap“ 与 "endWrap" 两个选项,用于实现不同样式的注释。
This commit is contained in:
Carm Jos 2022-04-23 20:35:48 +08:00
parent 760ac815df
commit 390815b790
30 changed files with 358 additions and 120 deletions

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>2.2.0</version>
<version>2.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>

View File

@ -2,6 +2,7 @@ package cc.carm.lib.configuration.core;
import cc.carm.lib.configuration.core.annotation.ConfigComment;
import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
@ -62,7 +63,7 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
@Nullable ConfigPath fieldPath, @Nullable ConfigComment filedComments,
boolean saveDefaults, boolean loadSubClasses) {
String path = getClassPath(clazz, parentPath, fieldName, fieldPath);
if (path != null) setComments(path, getClassComments(clazz, filedComments));
this.provider.setComment(path, getClassComments(clazz, filedComments));
for (Field field : clazz.getDeclaredFields()) {
initializeField(clazz, field, path, saveDefaults, loadSubClasses);
}
@ -82,21 +83,21 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
protected void initializeValue(@NotNull ConfigValue<?> value, @NotNull String path,
@NotNull String[] comments, boolean saveDefaults) {
@Nullable ConfigCommentInfo comments, boolean saveDefaults) {
value.initialize(provider, saveDefaults, path, comments);
}
private void initializeField(@NotNull Class<?> source, @NotNull Field field, @Nullable String parent,
boolean saveDefaults, boolean loadSubClasses) {
try {
field.setAccessible(true);
Object object = field.get(source);
if (object instanceof ConfigValue<?>) {
String path = getFieldPath(field, parent);
String[] comments = readComments(field.getAnnotation(ConfigComment.class));
initializeValue((ConfigValue<?>) object, path, comments, saveDefaults);
setComments(path, comments);
initializeValue(
(ConfigValue<?>) object, getFieldPath(field, parent),
ConfigCommentInfo.fromAnnotation(field.getAnnotation(ConfigComment.class)),
saveDefaults
);
} else if (object instanceof Class<?>) {
initializeClass(
(Class<?>) object, parent, field.getName(),
@ -109,26 +110,15 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
}
}
protected void setComments(@NotNull String path, @Nullable ConfigComment filedComments) {
setComments(path, readComments(filedComments));
protected void setComments(@Nullable String path, @Nullable ConfigCommentInfo comments) {
if (comments != null) this.provider.setComment(path, comments);
}
protected void setComments(@NotNull String path, @NotNull String[] comments) {
if (comments.length <= 0) return;
this.provider.setComments(path, comments);
}
protected static @NotNull String[] readComments(@Nullable ConfigComment filedComments) {
if (filedComments == null) return new String[0];
if (String.join("", filedComments.value()).length() <= 0) return new String[0];
return filedComments.value();
}
protected static @NotNull String[] getClassComments(@NotNull Class<?> clazz,
@Nullable ConfigComment fieldAnnotation) {
String[] clazzComments = readComments(clazz.getAnnotation(ConfigComment.class));
if (clazzComments.length > 0) return clazzComments;
else return readComments(fieldAnnotation);
protected static @Nullable ConfigCommentInfo getClassComments(@NotNull Class<?> clazz,
@Nullable ConfigComment fieldAnnotation) {
ConfigCommentInfo classComments = ConfigCommentInfo.fromAnnotation(clazz.getAnnotation(ConfigComment.class));
if (classComments != null) return classComments;
return ConfigCommentInfo.fromAnnotation(fieldAnnotation);
}
protected static @Nullable String getClassPath(@NotNull Class<?> clazz,

View File

@ -14,4 +14,32 @@ public @interface ConfigComment {
@NotNull
String[] value() default "";
/**
* 首行换行即会在注释开始前进行一次换行与上方配置分离优化观感
*
* <blockquote><pre>
* some-key: "SomeValue"
*
* # 注释第一行
* # 注释第二行
* startWrap: true
* </pre></blockquote>
*
* @return 是否在结尾添加换行符
*/
boolean startWrap() default true;
/**
* 末尾换行即会在注释结束后进行一次换行
* <blockquote><pre>
* # 注释第一行
* # 注释第二行
*
* endWrap: true
* </pre></blockquote>
* <p> 该功能可用于编写配置文件的顶部注释
*
* @return 是否在结尾添加换行符
*/
boolean endWrap() default false;
}

View File

@ -1,5 +1,6 @@
package cc.carm.lib.configuration.core.builder;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
@ -13,6 +14,9 @@ public abstract class AbstractConfigBuilder<T, B extends AbstractConfigBuilder<T
protected @Nullable String path;
protected @NotNull String[] comments = new String[0];
protected boolean startWrap = true;
protected boolean endWrap = false;
protected @Nullable T defaultValue;
public AbstractConfigBuilder(Class<? super P> providerClass) {
@ -38,10 +42,34 @@ public abstract class AbstractConfigBuilder<T, B extends AbstractConfigBuilder<T
return getThis();
}
public @NotNull B setStartWarp(boolean enable) {
this.startWrap = enable;
return getThis();
}
public @NotNull B startWarp() {
return setStartWarp(true);
}
public @NotNull B setEndWarp(boolean enable) {
this.endWrap = enable;
return getThis();
}
public @NotNull B endWarp() {
return setEndWarp(true);
}
public @NotNull B defaults(@Nullable T defaultValue) {
this.defaultValue = defaultValue;
return getThis();
}
protected @Nullable ConfigCommentInfo buildComments() {
ConfigCommentInfo info = ConfigCommentInfo.of(this.comments, this.startWrap, this.endWrap);
if (info.equals(ConfigCommentInfo.defaults())) return null;
else return info;
}
}

View File

@ -1,46 +1,12 @@
package cc.carm.lib.configuration.core.builder;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class CommonConfigBuilder<T, B extends CommonConfigBuilder<T, B>>
extends AbstractConfigBuilder<T, B, ConfigurationProvider<?>> {
protected @Nullable ConfigurationProvider<?> provider;
protected @Nullable String path;
protected @NotNull String[] comments = new String[0];
protected @Nullable T defaultValue;
public CommonConfigBuilder() {
super(ConfigurationProvider.class);
}
protected abstract @NotNull B getThis();
public abstract @NotNull ConfigValue<?> build();
public @NotNull B from(@Nullable ConfigurationProvider<?> provider) {
this.provider = provider;
return getThis();
}
public @NotNull B path(@Nullable String path) {
this.path = path;
return getThis();
}
public @NotNull B comments(@NotNull String... comments) {
this.comments = comments;
return getThis();
}
public @NotNull B defaults(@Nullable T defaultValue) {
this.defaultValue = defaultValue;
return getThis();
}
}

View File

@ -65,7 +65,7 @@ public class SourceListBuilder<S, V> extends CommonConfigBuilder<List<V>, Source
@Override
public @NotNull ConfiguredList<V> build() {
return new ConfiguredList<>(
this.provider, this.path, this.comments,
this.provider, this.path, this.buildComments(),
this.valueClass, this.defaultValue,
this.sourceParser.andThen(this.valueParser),
this.valueSerializer.andThen(sourceSerializer)

View File

@ -89,7 +89,7 @@ public class SourceMapBuilder<M extends Map<K, V>, S, K, V> extends CommonConfig
@Override
public @NotNull ConfiguredMap<K, V> build() {
return new ConfiguredMap<>(
this.provider, this.path, this.comments,
this.provider, this.path,this.buildComments(),
this.defaultValue, this.supplier,
this.keyClass, this.keyParser,
this.valueClass, this.sourceParser.andThen(this.valueParser),

View File

@ -45,7 +45,7 @@ public class SectionValueBuilder<V>
@Override
public @NotNull ConfiguredSection<V> build() {
return new ConfiguredSection<>(
this.provider, this.path, this.comments,
this.provider, this.path,this.buildComments(),
this.valueClass, this.defaultValue,
this.parser, this.serializer
);

View File

@ -3,6 +3,7 @@ package cc.carm.lib.configuration.core.builder.value;
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
import org.jetbrains.annotations.NotNull;
@ -57,7 +58,7 @@ public class SourceValueBuilder<S, V> extends CommonConfigBuilder<V, SourceValue
@Override
public @NotNull ConfiguredValue<V> build() {
return new ConfiguredValue<>(
this.provider, this.path, this.comments,
this.provider, this.path, this.buildComments(),
this.valueClass, this.defaultValue,
this.valueParser.compose(this.sourceParser),
this.valueSerializer.andThen(sourceSerializer)

View File

@ -0,0 +1,80 @@
package cc.carm.lib.configuration.core.source;
import cc.carm.lib.configuration.core.annotation.ConfigComment;
import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
public class ConfigCommentInfo {
public static @NotNull ConfigCommentInfo DEFAULT_INFO = of(new String[0], true, false);
protected final @NotNull String[] comments;
protected final boolean startWrap;
protected final boolean endWrap;
public ConfigCommentInfo(@NotNull String[] comments, boolean startWrap, boolean endWrap) {
this.comments = comments;
this.startWrap = startWrap;
this.endWrap = endWrap;
}
public @NotNull String[] getComments() {
return comments;
}
public boolean endWrap() {
return endWrap;
}
public boolean startWrap() {
return startWrap;
}
public static @NotNull ConfigCommentInfo of(@NotNull String[] comments, boolean startWrap, boolean endWrap) {
return new ConfigCommentInfo(comments, startWrap, endWrap);
}
public static @NotNull ConfigCommentInfo defaults() {
return DEFAULT_INFO;
}
@Contract("!null->!null")
public static @Nullable ConfigCommentInfo fromAnnotation(@Nullable ConfigComment comment) {
if (comment == null) return null;
else return new ConfigCommentInfo(comment.value(), comment.startWrap(), comment.endWrap());
}
public static @NotNull ConfigCommentInfo fromValue(@NotNull ConfigValue<?> value) {
return Optional.ofNullable(value.getComments()).orElse(defaults());
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ConfigCommentInfo that = (ConfigCommentInfo) o;
return startWrap == that.startWrap && endWrap == that.endWrap && Arrays.equals(getComments(), that.getComments());
}
@Override
public int hashCode() {
int result = Objects.hash(startWrap, endWrap);
result = 31 * result + Arrays.hashCode(getComments());
return result;
}
@Override
public String toString() {
return "ConfigCommentInfo{" +
"comments=" + Arrays.toString(comments) +
", startWrap=" + startWrap +
", endWrap=" + endWrap +
'}';
}
}

View File

@ -27,9 +27,9 @@ public abstract class ConfigurationProvider<W extends ConfigurationWrapper> {
public abstract void save() throws Exception;
public abstract void setComments(@NotNull String path, @NotNull String... comments);
public abstract void setComment(@Nullable String path, @Nullable ConfigCommentInfo comment);
public abstract @Nullable String[] getComments(@NotNull String path);
public abstract @Nullable ConfigCommentInfo getComment(@Nullable String path);
public abstract @NotNull ConfigInitializer<? extends ConfigurationProvider<W>> getInitializer();

View File

@ -1,6 +1,7 @@
package cc.carm.lib.configuration.core.value;
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.jetbrains.annotations.NotNull;
@ -17,12 +18,13 @@ public abstract class ConfigValue<T> {
protected @Nullable ConfigurationProvider<?> provider;
protected @Nullable String configPath;
protected @NotNull String[] comments;
protected @Nullable ConfigCommentInfo comments;
protected @Nullable T defaultValue;
public ConfigValue(@Nullable ConfigurationProvider<?> provider,
@Nullable String configPath, @NotNull String[] comments, @Nullable T defaultValue) {
@Nullable String configPath, @Nullable ConfigCommentInfo comments,
@Nullable T defaultValue) {
this.provider = provider;
this.configPath = configPath;
this.comments = comments;
@ -30,11 +32,12 @@ public abstract class ConfigValue<T> {
}
public void initialize(@NotNull ConfigurationProvider<?> provider, boolean saveDefault,
@NotNull String configPath, @NotNull String... comments) {
@NotNull String configPath, @Nullable ConfigCommentInfo comments) {
if (this.provider == null) this.provider = provider;
if (this.configPath == null) this.configPath = configPath;
if (this.comments.length == 0) this.comments = comments;
if (this.comments == null) this.comments = comments;
if (saveDefault) setDefault();
if (getComments() != null) this.provider.setComment(getConfigPath(), getComments());
}
public @Nullable T getDefaultValue() {
@ -103,6 +106,11 @@ public abstract class ConfigValue<T> {
Optional.ofNullable(getDefaultValue()).ifPresent(this::set);
}
/**
* 判断加载的配置是否与默认值相同
*
* @return 获取当前值是否为默认值
*/
public boolean isDefault() {
T defaultValue = getDefaultValue();
T value = get();
@ -137,12 +145,8 @@ public abstract class ConfigValue<T> {
getConfiguration().set(getConfigPath(), value);
}
public String[] getComments() {
public @Nullable ConfigCommentInfo getComments() {
return comments;
}
public void setComments(String[] comments) {
this.comments = comments;
}
}

View File

@ -1,5 +1,6 @@
package cc.carm.lib.configuration.core.value.impl;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
@ -12,7 +13,7 @@ public abstract class CachedConfigValue<T> extends ConfigValue<T> {
protected long parsedTime = -1;
public CachedConfigValue(@Nullable ConfigurationProvider<?> provider, @Nullable String sectionPath,
@NotNull String[] comments, @Nullable T defaultValue) {
@Nullable ConfigCommentInfo comments, @Nullable T defaultValue) {
super(provider, sectionPath, comments, defaultValue);
}

View File

@ -2,6 +2,7 @@ package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
@ -22,7 +23,7 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
protected final @NotNull ConfigDataFunction<V, Object> serializer;
public ConfiguredList(@Nullable ConfigurationProvider<?> provider,
@Nullable String sectionPath, @NotNull String[] comments,
@Nullable String sectionPath, @Nullable ConfigCommentInfo comments,
@NotNull Class<V> valueClass, @Nullable List<V> defaultValue,
@NotNull ConfigDataFunction<Object, V> parser,
@NotNull ConfigDataFunction<V, Object> serializer) {

View File

@ -2,6 +2,7 @@ package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.builder.map.ConfigMapBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
@ -32,7 +33,7 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> {
protected final @NotNull ConfigDataFunction<V, Object> valueSerializer;
public ConfiguredMap(@Nullable ConfigurationProvider<?> provider,
@Nullable String sectionPath, @NotNull String[] comments,
@Nullable String sectionPath, @Nullable ConfigCommentInfo comments,
@Nullable Map<K, V> defaultValue, @NotNull Supplier<? extends Map<K, V>> supplier,
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<Object, V> valueParser,

View File

@ -3,6 +3,7 @@ package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.builder.value.SectionValueBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
@ -24,7 +25,7 @@ public class ConfiguredSection<V> extends CachedConfigValue<V> {
protected final @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
public ConfiguredSection(@Nullable ConfigurationProvider<?> provider,
@Nullable String sectionPath, @NotNull String[] comments,
@Nullable String sectionPath, @Nullable ConfigCommentInfo comments,
@NotNull Class<V> valueClass, @Nullable V defaultValue,
@NotNull ConfigValueParser<ConfigurationWrapper, V> parser,
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {

View File

@ -3,6 +3,7 @@ package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
@ -30,7 +31,7 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
protected final @NotNull ConfigDataFunction<V, Object> serializer;
public ConfiguredValue(@Nullable ConfigurationProvider<?> provider,
@Nullable String sectionPath, @NotNull String[] comments,
@Nullable String sectionPath, @Nullable ConfigCommentInfo comments,
@NotNull Class<V> valueClass, @Nullable V defaultValue,
@NotNull ConfigValueParser<Object, V> parser,
@NotNull ConfigDataFunction<V, Object> serializer) {

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>2.2.0</version>
<version>2.3.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -1,6 +1,7 @@
package cc.carm.lib.configuration.json;
import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
import com.google.gson.Gson;
@ -68,15 +69,16 @@ public class JSONConfigProvider extends FileConfigProvider<JSONConfigWrapper> {
}
@Override
public void setComments(@NotNull String path, @NotNull String... comments) {
// JSON doesn't support comments.
public void setComment(@Nullable String path, @Nullable ConfigCommentInfo comment) {
// JSON doesn't support comments;
}
@Override
public @Nullable String[] getComments(@NotNull String path) {
return new String[0]; // JSON doesn't support comments.
public @Nullable ConfigCommentInfo getComment(@Nullable String path) {
return null;
}
@Override
public @NotNull ConfigInitializer<? extends ConfigurationProvider<JSONConfigWrapper>> getInitializer() {
return this.initializer;

View File

@ -4,7 +4,10 @@ import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Some code comes from BungeeCord's implementation of the JsonConfiguration.
@ -33,12 +36,16 @@ public class JSONConfigWrapper implements ConfigurationWrapper {
@Override
public @NotNull Set<String> getKeys(boolean deep) {
return new LinkedHashSet<>(this.data.keySet());
return getValues(deep).keySet();
}
@Override
public @NotNull Map<String, Object> getValues(boolean deep) {
return new LinkedHashMap<>(this.data);
if (deep) {
Map<String, Object> values = new LinkedHashMap<>();
mapChildrenValues(values, this, null, true);
return values;
} else return new LinkedHashMap<>(this.data);
}
@Override
@ -111,4 +118,16 @@ public class JSONConfigWrapper implements ConfigurationWrapper {
return (index == -1) ? path : path.substring(index + 1);
}
protected void mapChildrenValues(@NotNull Map<String, Object> output, @NotNull JSONConfigWrapper section,
@Nullable String parent, boolean deep) {
for (Map.Entry<String, Object> entry : section.data.entrySet()) {
String path = (parent == null ? "" : parent + ".") + entry.getKey();
output.remove(path);
output.put(path, entry.getValue());
if (deep && entry.getValue() instanceof JSONConfigWrapper) {
this.mapChildrenValues(output, (JSONConfigWrapper) entry.getValue(), path, true);
}
}
}
}

View File

@ -23,6 +23,11 @@ public class JSONConfigTest {
provider.initialize(DemoConfiguration.class);
testDemo();
System.out.println("----------------------------------------------------");
provider.getConfiguration().getValues(true).forEach((k, v) -> System.out.println(k + ": " + v));
System.out.println("----------------------------------------------------");
provider.getConfiguration().getValues(false).forEach((k, v) -> System.out.println(k + ": " + v));
System.out.println("----------------------------------------------------");
try {
provider.save();

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>2.2.0</version>
<version>2.3.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -27,9 +27,9 @@
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>yamlconfiguration-commented</artifactId>
<version>2.0.2</version>
<groupId>org.bspfsystems</groupId>
<artifactId>yamlconfiguration</artifactId>
<version>1.0.11</version>
<scope>compile</scope>
</dependency>

View File

@ -1,35 +1,114 @@
package cc.carm.lib.configuration.yaml;
import org.bspfsystems.yamlconfiguration.commented.CommentsProvider;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import org.bspfsystems.yamlconfiguration.configuration.ConfigurationSection;
import org.bspfsystems.yamlconfiguration.file.FileConfiguration;
import org.bspfsystems.yamlconfiguration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class YAMLComments implements CommentsProvider {
import static cc.carm.lib.configuration.yaml.YAMLConfigProvider.SEPARATOR;
Map<String, String[]> comments = new HashMap<>();
public class YAMLComments {
protected Map<String, String[]> getComments() {
Map<String, ConfigCommentInfo> comments = new HashMap<>();
protected Map<String, ConfigCommentInfo> getComments() {
return comments;
}
public void set(@NotNull String path, @NotNull String... comments) {
if (comments.length == 0) {
public void set(@Nullable String path, @Nullable ConfigCommentInfo comments) {
if (comments == null) {
getComments().remove(path);
} else {
getComments().put(path, comments);
}
}
public @Nullable String[] get(@NotNull String path) {
return getComments().get(path);
public @NotNull ConfigCommentInfo get(@Nullable String path) {
return getComments().getOrDefault(path, ConfigCommentInfo.defaults());
}
@Override
public String[] apply(String s) {
return get(s);
public @Nullable String buildComments(@NotNull String indents, @Nullable String path) {
ConfigCommentInfo comments = get(path);
if (!String.join("", comments.getComments()).isEmpty()) {
String prefix = comments.startWrap() ? "\n" : "";
String suffix = comments.endWrap() ? "\n" : "";
StringJoiner joiner = new StringJoiner("\n", prefix, suffix);
for (String comment : comments.getComments()) {
if (comment.length() == 0) joiner.add(" ");
else joiner.add(indents + "# " + comment);
}
return joiner + "\n";
} else {
return comments.startWrap() || comments.endWrap() ? "\n" : null;
}
}
/**
* 从一个文件读取配置并写入注释到某个写入器中
* 该方法的源代码来自 tchristofferson/ConfigUpdater 项目
*
* @param source 源配置文件
* @param writer 配置写入器
* @throws IOException 当写入发生错误时抛出
*/
public void writeComments(@NotNull YamlConfiguration source, @NotNull BufferedWriter writer) throws IOException {
FileConfiguration parserConfig = new YamlConfiguration();
for (String fullKey : source.getKeys(true)) {
String indents = getIndents(fullKey);
String comment = buildComments(indents, fullKey);
if (comment != null) writer.write(comment);
Object currentValue = source.get(fullKey);
String[] splitFullKey = fullKey.split("[" + SEPARATOR + "]");
String trailingKey = splitFullKey[splitFullKey.length - 1];
if (currentValue instanceof ConfigurationSection) {
writer.write(indents + trailingKey + ":");
if (!((ConfigurationSection) currentValue).getKeys(false).isEmpty())
writer.write("\n");
else
writer.write(" {}\n");
continue;
}
parserConfig.set(trailingKey, currentValue);
String yaml = parserConfig.saveToString();
yaml = yaml.substring(0, yaml.length() - 1).replace("\n", "\n" + indents);
String toWrite = indents + yaml + "\n";
parserConfig.set(trailingKey, null);
writer.write(toWrite);
}
String endComment = buildComments("", null);
if (endComment != null) writer.write(endComment);
writer.close();
}
/**
* 得到一个键的缩进
* 该方法的源代码来自 tchristofferson/ConfigUpdater 项目
*
* @param key
* @return 该键的缩进文本
*/
protected static String getIndents(String key) {
String[] splitKey = key.split("[" + YAMLConfigProvider.SEPARATOR + "]");
return IntStream.range(1, splitKey.length).mapToObj(i -> " ").collect(Collectors.joining());
}
}

View File

@ -1,17 +1,25 @@
package cc.carm.lib.configuration.yaml;
import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
import org.bspfsystems.yamlconfiguration.commented.CommentedYamlConfiguration;
import org.bspfsystems.yamlconfiguration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedWriter;
import java.io.File;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public class YAMLConfigProvider extends FileConfigProvider<YAMLSectionWrapper> {
protected static final char SEPARATOR = '.';
protected final @NotNull YAMLComments comments = new YAMLComments();
protected CommentedYamlConfiguration configuration;
protected YamlConfiguration configuration;
protected ConfigInitializer<YAMLConfigProvider> initializer;
public YAMLConfigProvider(@NotNull File file) {
@ -19,7 +27,7 @@ public class YAMLConfigProvider extends FileConfigProvider<YAMLSectionWrapper> {
}
public void initializeConfig() {
this.configuration = CommentedYamlConfiguration.loadConfiguration(comments, file);
this.configuration = YamlConfiguration.loadConfiguration(file);
this.initializer = new ConfigInitializer<>(this);
}
@ -36,15 +44,29 @@ public class YAMLConfigProvider extends FileConfigProvider<YAMLSectionWrapper> {
@Override
public void save() throws Exception {
configuration.save(getFile());
// tchristofferson/ConfigUpdater start
StringWriter writer = new StringWriter();
this.comments.writeComments(configuration, new BufferedWriter(writer));
String value = writer.toString(); // config contents
Path toUpdatePath = getFile().toPath();
if (!value.equals(new String(Files.readAllBytes(toUpdatePath), StandardCharsets.UTF_8))) {
// if updated contents are not the same as current file contents, update
Files.write(toUpdatePath, value.getBytes(StandardCharsets.UTF_8));
}
// tchristofferson/ConfigUpdater end
}
@Override
public void setComments(@NotNull String path, @NotNull String... comments) {
public void setComment(@Nullable String path, @Nullable ConfigCommentInfo comments) {
this.comments.set(path, comments);
}
@Override
public @Nullable String[] getComments(@NotNull String path) {
public @Nullable ConfigCommentInfo getComment(@Nullable String path) {
return this.comments.get(path);
}

View File

@ -1,5 +1,6 @@
package cc.carm.lib.configuration.yaml;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
import cc.carm.lib.configuration.yaml.builder.YAMLConfigBuilder;
@ -13,7 +14,8 @@ public abstract class YAMLValue<T> extends CachedConfigValue<T> {
}
public YAMLValue(@Nullable YAMLConfigProvider provider,
@Nullable String configPath, @NotNull String[] comments, @Nullable T defaultValue) {
@Nullable String configPath, @Nullable ConfigCommentInfo comments,
@Nullable T defaultValue) {
super(provider, configPath, comments, defaultValue);
}

View File

@ -21,7 +21,7 @@ public class SerializableBuilder<T extends ConfigurationSerializable>
@Override
public @NotNull ConfiguredSerializable<T> build() {
return new ConfiguredSerializable<>(this.provider, this.path, this.comments, this.valueClass, this.defaultValue);
return new ConfiguredSerializable<>(this.provider, this.path, buildComments(), this.valueClass, this.defaultValue);
}

View File

@ -1,7 +1,8 @@
package cc.carm.lib.configuration.yaml.value;
import cc.carm.lib.configuration.yaml.YAMLValue;
import cc.carm.lib.configuration.core.source.ConfigCommentInfo;
import cc.carm.lib.configuration.yaml.YAMLConfigProvider;
import cc.carm.lib.configuration.yaml.YAMLValue;
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -15,14 +16,14 @@ public class ConfiguredSerializable<T extends ConfigurationSerializable> extends
}
public static <V extends ConfigurationSerializable> ConfiguredSerializable<V> of(@NotNull Class<V> valueClass,
@Nullable V defaultValue) {
@Nullable V defaultValue) {
return builder().ofSerializable(valueClass).defaults(defaultValue).build();
}
protected final @NotNull Class<T> valueClass;
public ConfiguredSerializable(@Nullable YAMLConfigProvider provider,
@Nullable String configPath, @NotNull String[] comments,
@Nullable String configPath, @Nullable ConfigCommentInfo comments,
@NotNull Class<T> valueClass, @Nullable T defaultValue) {
super(provider, configPath, comments, defaultValue);
this.valueClass = valueClass;

View File

@ -10,22 +10,26 @@ import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
public class DatabaseConfiguration extends ConfigurationRoot {
@ConfigPath("driver")
@ConfigComment({
@ConfigComment(value = {
"数据库驱动配置,请根据数据库类型设置。",
"- MySQL: com.mysql.cj.jdbc.Driver",
"- MariaDB(推荐): org.mariadb.jdbc.Driver",
})
}, startWrap = false)
protected static final ConfigValue<String> DRIVER_NAME = ConfiguredValue.of(
String.class, "com.mysql.cj.jdbc.Driver"
);
@ConfigComment(startWrap = false)
protected static final ConfigValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
@ConfigComment(startWrap = false)
protected static final ConfigValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
@ConfigComment(startWrap = false)
protected static final ConfigValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
@ConfigComment(startWrap = false)
protected static final ConfigValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
@ConfigComment(startWrap = false)
protected static final ConfigValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
@ConfigComment(startWrap = false)
protected static final ConfigValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
protected static String buildJDBC() {

View File

@ -16,14 +16,16 @@ import java.util.Map;
import java.util.Objects;
import java.util.UUID;
@ConfigComment({"给根类添加的注释将显示在文件的末尾。"})
public class DemoConfiguration extends ConfigurationRoot {
@ConfigPath(root = true)
@ConfigComment({
@ConfigComment(value = {
"有时候,需要在配置文件最上面显示点东西,",
"此时就推荐添加一个可以用到但并不重要的参数到最上面",
"并给他添加对应的注释。"
})
}, startWrap = false, endWrap = true)
protected static final ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);

View File

@ -15,7 +15,7 @@
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId>
<packaging>pom</packaging>
<version>2.2.0</version>
<version>2.3.0</version>
<modules>
<module>core</module>
<module>impl/yaml</module>