1
mirror of https://github.com/CarmJos/EasyConfiguration.git synced 2026-06-04 18:48:20 +08:00

Compare commits

...

9 Commits

Author SHA1 Message Date
carm 04eaf6606d feat: Jump to 4.0.11 2025-03-06 18:19:25 +08:00
carm e558e93410 feat(sql): Support custom sql table schema 2025-03-06 18:19:06 +08:00
flowerinsnow e55fe3a8d5 feat: hocon supported 2025-03-06 18:03:27 +08:00
carm 8f075b99b5 chore(deps): use parent dependencies' version 2025-03-04 11:58:01 +08:00
carm 09a0b3c373 chore(deps): use parent dependencies' version 2025-03-04 11:34:16 +08:00
carm 4df4977733 feat(msg): add enhanced text support 2025-03-04 04:21:58 +08:00
carm 6434feb980 fix(sql): Fixed sql table format 2025-03-04 01:57:04 +08:00
carm bc3e4b3e6f fix(comment): Fixed yaml comments 2025-03-04 01:24:12 +08:00
carm c2a9e2254c feat(section): Add ConfigureSection#asMap function 2025-03-04 01:06:21 +08:00
39 changed files with 455 additions and 652 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.8</version> <version>4.0.11</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
@@ -48,7 +48,6 @@ public interface ConfigureSection {
return (parent().isRoot() ? "" : parent().fullPath() + pathSeparator()) + path(); return (parent().isRoot() ? "" : parent().fullPath() + pathSeparator()) + path();
} }
/** /**
* Get the path separator for the section. * Get the path separator for the section.
* *
@@ -143,6 +142,17 @@ public interface ConfigureSection {
return getValues(false); return getValues(false);
} }
/**
* Get this section as a map.
* <p>
* In this map, child {@link ConfigureSection}s will also be represented as {@link Map}s.
*
* @return Map of data values contained within this Section.
*/
@NotNull
@UnmodifiableView
Map<String, Object> asMap();
/** /**
* Create a stream of all values in this section. * Create a stream of all values in this section.
* *
@@ -153,7 +163,7 @@ public interface ConfigureSection {
} }
/** /**
* Iterates over all keys in this section. * Iterates over all key-values in this section (include child sections)
* *
* @param action The action to apply to each key. * @param action The action to apply to each key.
*/ */
@@ -107,6 +107,11 @@ public abstract class ConfigureSource<
return section().getKeys(deep); return section().getKeys(deep);
} }
@Override
public @NotNull @UnmodifiableView Map<String, Object> asMap() {
return section().asMap();
}
@Override @Override
public @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data) { public @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
return section().createSection(path, data); return section().createSection(path, data);
+1 -7
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.8</version> <version>4.0.11</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
@@ -39,12 +39,6 @@
<version>${project.parent.version}</version> <version>${project.parent.version}</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-feature-versioned</artifactId>
<version>4.0.8</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
@@ -36,6 +36,12 @@ public interface DemoConfiguration extends Configuration {
@FooterComments({"上述的枚举内容本质上是通过STRING解析的"}) @FooterComments({"上述的枚举内容本质上是通过STRING解析的"})
ConfigValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS); ConfigValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS);
@HeaderComments({"空值测试"})
@InlineComment("空值Inline注释")
ConfiguredMap<String, String> EMPTY = ConfiguredMap.builderOf(String.class, String.class)
.asLinkedMap().fromString()
.build();
@ConfigPath("registered_users") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。 @ConfigPath("registered_users") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
@HeaderComments({"Section类型数据测试"}) // 通过注解给配置添加注释。 @HeaderComments({"Section类型数据测试"}) // 通过注解给配置添加注释。
@InlineComment("默认地注释会加到Section的首行末尾") // 通过注解给配置添加注释。 @InlineComment("默认地注释会加到Section的首行末尾") // 通过注解给配置添加注释。
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.8</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -49,6 +49,17 @@ public interface CommentableMeta {
FooterComments.class, FOOTER, FooterComments.class, FOOTER,
a -> Arrays.asList(a.value()) a -> Arrays.asList(a.value())
); );
initializer.registerAnnotation(InlineComment.class, INLINE, a -> {
Map<String, String> map = new HashMap<>();
if (a.regex().length == 0) { // for current path
map.put(null, a.value());
return map;
}
for (String regex : a.regex()) { // for specified path
map.put(regex, a.value());
}
return map;
});
initializer.registerAnnotation(InlineComments.class, INLINE, a -> { initializer.registerAnnotation(InlineComments.class, INLINE, a -> {
Map<String, String> map = new HashMap<>(); Map<String, String> map = new HashMap<>();
for (InlineComment comment : a.value()) { for (InlineComment comment : a.value()) {
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.8</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.8</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -75,17 +75,18 @@ public abstract class AbstractMapSection<R extends AbstractMapSection<R>> implem
return deep ? getKeys(true).size() : this.data.size(); return deep ? getKeys(true).size() : this.data.size();
} }
@Override
@UnmodifiableView @UnmodifiableView
public @NotNull Map<String, Object> rawMap() { public @NotNull Map<String, Object> asMap() {
Map<String, Object> output = new LinkedHashMap<>(); Map<String, Object> output = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : this.data.entrySet()) { for (Map.Entry<String, Object> entry : this.data.entrySet()) {
if (entry.getValue() instanceof AbstractMapSection<?>) { if (entry.getValue() instanceof AbstractMapSection<?>) {
output.put(entry.getKey(), ((AbstractMapSection<?>) entry.getValue()).rawMap()); output.put(entry.getKey(), ((AbstractMapSection<?>) entry.getValue()).asMap());
} else if (entry.getValue() instanceof List<?>) { } else if (entry.getValue() instanceof List<?>) {
List<Object> list = new ArrayList<>(); List<Object> list = new ArrayList<>();
for (Object obj : (List<?>) entry.getValue()) { for (Object obj : (List<?>) entry.getValue()) {
if (obj instanceof AbstractMapSection<?>) { if (obj instanceof AbstractMapSection<?>) {
list.add(((AbstractMapSection<?>) obj).rawMap()); list.add(((AbstractMapSection<?>) obj).asMap());
} else { } else {
list.add(obj); list.add(obj);
} }
@@ -20,13 +20,18 @@ public class ImmutableSection implements ConfigureSection {
} }
protected final @Nullable ImmutableSection parent; protected final @Nullable ImmutableSection parent;
protected final @NotNull ConfigureSection section; protected final @NotNull ConfigureSection raw;
private ImmutableSection(@Nullable ImmutableSection parent, @NotNull ConfigureSection section) { private ImmutableSection(@Nullable ImmutableSection parent, @NotNull ConfigureSection raw) {
this.parent = parent; this.parent = parent;
this.section = section; this.raw = raw;
} }
private @NotNull ConfigureSection raw() {
return raw;
}
@Override @Override
public @Nullable ImmutableSection parent() { public @Nullable ImmutableSection parent() {
return this.parent; return this.parent;
@@ -34,16 +39,12 @@ public class ImmutableSection implements ConfigureSection {
@Override @Override
public @NotNull String path() { public @NotNull String path() {
return section().path(); return raw().path();
}
private @NotNull ConfigureSection section() {
return section;
} }
@Override @Override
public @NotNull @UnmodifiableView Map<String, Object> getValues(boolean deep) { public @NotNull @UnmodifiableView Map<String, Object> getValues(boolean deep) {
return section().getValues(deep); return raw().getValues(deep);
} }
@Override @Override
@@ -58,12 +59,12 @@ public class ImmutableSection implements ConfigureSection {
@Override @Override
public @NotNull ImmutableSection createSection(@NotNull String path, @NotNull Map<?, ?> data) { public @NotNull ImmutableSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
return new ImmutableSection(this, section().createSection(path, data)); return new ImmutableSection(this, raw().createSection(path, data));
} }
@Override @Override
public @Nullable Object get(@NotNull String path) { public @Nullable Object get(@NotNull String path) {
Object value = section().get(path); Object value = raw().get(path);
if (value instanceof ConfigureSection && !(value instanceof ImmutableSection)) { if (value instanceof ConfigureSection && !(value instanceof ImmutableSection)) {
return new ImmutableSection(this, (ConfigureSection) value); return new ImmutableSection(this, (ConfigureSection) value);
} }
@@ -72,7 +73,7 @@ public class ImmutableSection implements ConfigureSection {
@Override @Override
public @Nullable ConfigureSection getSection(@NotNull String path) { public @Nullable ConfigureSection getSection(@NotNull String path) {
ConfigureSection get = section().getSection(path); ConfigureSection get = raw().getSection(path);
if (get != null && !(get instanceof ImmutableSection)) { if (get != null && !(get instanceof ImmutableSection)) {
return new ImmutableSection(this, get); return new ImmutableSection(this, get);
} }
@@ -81,297 +82,302 @@ public class ImmutableSection implements ConfigureSection {
@Override @Override
public char pathSeparator() { public char pathSeparator() {
return section().pathSeparator(); return raw().pathSeparator();
} }
@Override @Override
public boolean isRoot() { public boolean isRoot() {
return section().isRoot(); return raw().isRoot();
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return section().isEmpty(); return raw().isEmpty();
} }
@Override @Override
public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) { public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) {
return section().getKeys(deep); return raw().getKeys(deep);
} }
@Override @Override
public @NotNull @UnmodifiableView Set<String> keys() { public @NotNull @UnmodifiableView Set<String> keys() {
return section().keys(); return raw().keys();
} }
@Override @Override
public @NotNull @UnmodifiableView Map<String, Object> values() { public @NotNull @UnmodifiableView Map<String, Object> values() {
return section().values(); return raw().values();
}
@Override
public @NotNull @UnmodifiableView Map<String, Object> asMap() {
return raw().asMap();
} }
@Override @Override
public Stream<Map.Entry<String, Object>> stream() { public Stream<Map.Entry<String, Object>> stream() {
return section().stream(); return raw().stream();
} }
@Override @Override
public void forEach(@NotNull BiConsumer<String, Object> action) { public void forEach(@NotNull BiConsumer<String, Object> action) {
section().forEach(action); raw().forEach(action);
} }
@Override @Override
public boolean contains(@NotNull String path) { public boolean contains(@NotNull String path) {
return section().contains(path); return raw().contains(path);
} }
@Override @Override
public boolean containsValue(@NotNull String path) { public boolean containsValue(@NotNull String path) {
return section().containsValue(path); return raw().containsValue(path);
} }
@Override @Override
public <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) { public <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
return section().isType(path, typeClass); return raw().isType(path, typeClass);
} }
@Override @Override
public boolean isList(@NotNull String path) { public boolean isList(@NotNull String path) {
return section().isList(path); return raw().isList(path);
} }
@Override @Override
public @Nullable List<?> getList(@NotNull String path) { public @Nullable List<?> getList(@NotNull String path) {
return section().getList(path); return raw().getList(path);
} }
@Override @Override
public boolean isSection(@NotNull String path) { public boolean isSection(@NotNull String path) {
return section().isSection(path); return raw().isSection(path);
} }
@Override @Override
public <T> @Nullable T get(@NotNull String path, @NotNull Class<T> type) { public <T> @Nullable T get(@NotNull String path, @NotNull Class<T> type) {
return section().get(path, type); return raw().get(path, type);
} }
@Override @Override
public <T> @Nullable T get(@NotNull String path, @NotNull DataFunction<@Nullable Object, T> parser) { public <T> @Nullable T get(@NotNull String path, @NotNull DataFunction<@Nullable Object, T> parser) {
return section().get(path, parser); return raw().get(path, parser);
} }
@Override @Override
public <T> @Nullable T get(@NotNull String path, @Nullable T defaults, @NotNull Class<T> clazz) { public <T> @Nullable T get(@NotNull String path, @Nullable T defaults, @NotNull Class<T> clazz) {
return section().get(path, defaults, clazz); return raw().get(path, defaults, clazz);
} }
@Override @Override
public <T> @Nullable T get(@NotNull String path, @Nullable T defaultValue, @NotNull DataFunction<Object, T> parser) { public <T> @Nullable T get(@NotNull String path, @Nullable T defaultValue, @NotNull DataFunction<Object, T> parser) {
return section().get(path, defaultValue, parser); return raw().get(path, defaultValue, parser);
} }
@Override @Override
public boolean isBoolean(@NotNull String path) { public boolean isBoolean(@NotNull String path) {
return section().isBoolean(path); return raw().isBoolean(path);
} }
@Override @Override
public boolean getBoolean(@NotNull String path) { public boolean getBoolean(@NotNull String path) {
return section().getBoolean(path); return raw().getBoolean(path);
} }
@Override @Override
public @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) { public @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
return section().getBoolean(path, def); return raw().getBoolean(path, def);
} }
@Override @Override
public @Nullable Boolean isByte(@NotNull String path) { public @Nullable Boolean isByte(@NotNull String path) {
return section().isByte(path); return raw().isByte(path);
} }
@Override @Override
public @Nullable Byte getByte(@NotNull String path) { public @Nullable Byte getByte(@NotNull String path) {
return section().getByte(path); return raw().getByte(path);
} }
@Override @Override
public @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) { public @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
return section().getByte(path, def); return raw().getByte(path, def);
} }
@Override @Override
public boolean isShort(@NotNull String path) { public boolean isShort(@NotNull String path) {
return section().isShort(path); return raw().isShort(path);
} }
@Override @Override
public @Nullable Short getShort(@NotNull String path) { public @Nullable Short getShort(@NotNull String path) {
return section().getShort(path); return raw().getShort(path);
} }
@Override @Override
public @Nullable Short getShort(@NotNull String path, @Nullable Short def) { public @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
return section().getShort(path, def); return raw().getShort(path, def);
} }
@Override @Override
public boolean isInt(@NotNull String path) { public boolean isInt(@NotNull String path) {
return section().isInt(path); return raw().isInt(path);
} }
@Override @Override
public @Nullable Integer getInt(@NotNull String path) { public @Nullable Integer getInt(@NotNull String path) {
return section().getInt(path); return raw().getInt(path);
} }
@Override @Override
public @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) { public @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
return section().getInt(path, def); return raw().getInt(path, def);
} }
@Override @Override
public boolean isLong(@NotNull String path) { public boolean isLong(@NotNull String path) {
return section().isLong(path); return raw().isLong(path);
} }
@Override @Override
public @Nullable Long getLong(@NotNull String path) { public @Nullable Long getLong(@NotNull String path) {
return section().getLong(path); return raw().getLong(path);
} }
@Override @Override
public @Nullable Long getLong(@NotNull String path, @Nullable Long def) { public @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
return section().getLong(path, def); return raw().getLong(path, def);
} }
@Override @Override
public boolean isFloat(@NotNull String path) { public boolean isFloat(@NotNull String path) {
return section().isFloat(path); return raw().isFloat(path);
} }
@Override @Override
public @Nullable Float getFloat(@NotNull String path) { public @Nullable Float getFloat(@NotNull String path) {
return section().getFloat(path); return raw().getFloat(path);
} }
@Override @Override
public @Nullable Float getFloat(@NotNull String path, @Nullable Float def) { public @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
return section().getFloat(path, def); return raw().getFloat(path, def);
} }
@Override @Override
public boolean isDouble(@NotNull String path) { public boolean isDouble(@NotNull String path) {
return section().isDouble(path); return raw().isDouble(path);
} }
@Override @Override
public @Nullable Double getDouble(@NotNull String path) { public @Nullable Double getDouble(@NotNull String path) {
return section().getDouble(path); return raw().getDouble(path);
} }
@Override @Override
public @Nullable Double getDouble(@NotNull String path, @Nullable Double def) { public @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
return section().getDouble(path, def); return raw().getDouble(path, def);
} }
@Override @Override
public boolean isChar(@NotNull String path) { public boolean isChar(@NotNull String path) {
return section().isChar(path); return raw().isChar(path);
} }
@Override @Override
public @Nullable Character getChar(@NotNull String path) { public @Nullable Character getChar(@NotNull String path) {
return section().getChar(path); return raw().getChar(path);
} }
@Override @Override
public @Nullable Character getChar(@NotNull String path, @Nullable Character def) { public @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
return section().getChar(path, def); return raw().getChar(path, def);
} }
@Override @Override
public boolean isString(@NotNull String path) { public boolean isString(@NotNull String path) {
return section().isString(path); return raw().isString(path);
} }
@Override @Override
public @Nullable String getString(@NotNull String path) { public @Nullable String getString(@NotNull String path) {
return section().getString(path); return raw().getString(path);
} }
@Override @Override
public @Nullable String getString(@NotNull String path, @Nullable String def) { public @Nullable String getString(@NotNull String path, @Nullable String def) {
return section().getString(path, def); return raw().getString(path, def);
} }
@Override @Override
public @NotNull <V> List<V> getList(@NotNull String path, @NotNull DataFunction<Object, V> parser) { public @NotNull <V> List<V> getList(@NotNull String path, @NotNull DataFunction<Object, V> parser) {
return section().getList(path, parser); return raw().getList(path, parser);
} }
@Override @Override
public @NotNull List<String> getStringList(@NotNull String path) { public @NotNull List<String> getStringList(@NotNull String path) {
return section().getStringList(path); return raw().getStringList(path);
} }
@Override @Override
public @NotNull List<Integer> getIntegerList(@NotNull String path) { public @NotNull List<Integer> getIntegerList(@NotNull String path) {
return section().getIntegerList(path); return raw().getIntegerList(path);
} }
@Override @Override
public @NotNull List<Long> getLongList(@NotNull String path) { public @NotNull List<Long> getLongList(@NotNull String path) {
return section().getLongList(path); return raw().getLongList(path);
} }
@Override @Override
public @NotNull List<Double> getDoubleList(@NotNull String path) { public @NotNull List<Double> getDoubleList(@NotNull String path) {
return section().getDoubleList(path); return raw().getDoubleList(path);
} }
@Override @Override
public @NotNull List<Float> getFloatList(@NotNull String path) { public @NotNull List<Float> getFloatList(@NotNull String path) {
return section().getFloatList(path); return raw().getFloatList(path);
} }
@Override @Override
public @NotNull List<Byte> getByteList(@NotNull String path) { public @NotNull List<Byte> getByteList(@NotNull String path) {
return section().getByteList(path); return raw().getByteList(path);
} }
@Override @Override
public @NotNull List<Character> getCharList(@NotNull String path) { public @NotNull List<Character> getCharList(@NotNull String path) {
return section().getCharList(path); return raw().getCharList(path);
} }
@Override @Override
public <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path, @NotNull Supplier<C> constructor, @NotNull DataFunction<Object, T> parser) { public <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path, @NotNull Supplier<C> constructor, @NotNull DataFunction<Object, T> parser) {
return section().getCollection(path, constructor, parser); return raw().getCollection(path, constructor, parser);
} }
@Override @Override
public @NotNull Stream<?> stream(@NotNull String path) { public @NotNull Stream<?> stream(@NotNull String path) {
return section().stream(path); return raw().stream(path);
} }
@Override @Override
public @NotNull <T> Stream<T> stream(@NotNull String path, @NotNull Function<Object, T> parser) { public @NotNull <T> Stream<T> stream(@NotNull String path, @NotNull Function<Object, T> parser) {
return section().stream(path, parser); return raw().stream(path, parser);
} }
@Override @Override
public String childPath(String path) { public String childPath(String path) {
return section().childPath(path); return raw().childPath(path);
} }
@Override @Override
public int hashCode() { public int hashCode() {
return section.hashCode(); return raw.hashCode();
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
return Objects.equals(section, obj); return Objects.equals(raw, obj);
} }
} }
@@ -40,6 +40,12 @@ public class ShadedSection implements ConfigureSection {
return merge(template, source).getValues(deep); return merge(template, source).getValues(deep);
} }
@Override
public @NotNull @UnmodifiableView Map<String, Object> asMap() {
if (source == null) return template.asMap();
return merge(template, source).asMap();
}
@Override @Override
public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) { public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) {
Set<String> keys = new HashSet<>(template.getKeys(deep)); Set<String> keys = new HashSet<>(template.getKeys(deep));
+2 -2
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.8</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -29,7 +29,7 @@
<dependency> <dependency>
<groupId>${project.groupId}</groupId> <groupId>${project.groupId}</groupId>
<artifactId>easyconfiguration-yaml</artifactId> <artifactId>easyconfiguration-gson</artifactId>
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
@@ -16,11 +16,12 @@ import java.util.stream.IntStream;
public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEIVER, SELF>> { public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEIVER, SELF>> {
/** /**
* Used to match the message insertion * Used to match the message insertion.
* <p> * <p>
* format: * format:
* <br>- to insert parsed line {prefix}#content-id#{offset-above,offset-down} * <br>- to insert parsed line {prefix}#content-id#{offset-above,offset-down}
* <br>- to insert original line {prefix}@content-id@{offset-above,offset-down} * <br>- to insert original line {prefix}@content-id@{offset-above,offset-down}
* <br> original lines will not be parsed
* <br> example: * <br> example:
* <ul> * <ul>
* <li>{- }#content-id#{1,1}</li> * <li>{- }#content-id#{1,1}</li>
@@ -30,6 +31,21 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
public static final @NotNull Pattern INSERT_PATTERN = Pattern.compile( public static final @NotNull Pattern INSERT_PATTERN = Pattern.compile(
"^(?:\\{(?<prefix>.*)})?(?<type>[#@])(?<id>.*)[#@](?:\\{(?<above>-?\\d+)(?:,(?<down>-?\\d+))?})?$" "^(?:\\{(?<prefix>.*)})?(?<type>[#@])(?<id>.*)[#@](?:\\{(?<above>-?\\d+)(?:,(?<down>-?\\d+))?})?$"
); );
/**
* Used to match the message which can be inserted
* <p>
* format:
* <br>- ?[id]Message content
* <br> example:
* <ul>
* <li>?[click]Click to use this item!</li>
* </ul>
*/
public static final @NotNull Pattern ENABLE_PATTERN = Pattern.compile(
"^\\?\\[(?<id>.+)](?<content>.*)$"
);
public static final @NotNull UnaryOperator<String> DEFAULT_PARAM_BUILDER = s -> "%(" + s + ")"; public static final @NotNull UnaryOperator<String> DEFAULT_PARAM_BUILDER = s -> "%(" + s + ")";
protected BiFunction<RECEIVER, String, String> parser = (receiver, value) -> value; protected BiFunction<RECEIVER, String, String> parser = (receiver, value) -> value;
@@ -45,7 +61,7 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
/** /**
* Used to store the insertion of the message * Used to store the insertion of the message
*/ */
protected @NotNull Map<String, Function<RECEIVER, List<String>>> insertion = new HashMap<>(); protected @NotNull Map<String, @Nullable Function<RECEIVER, List<String>>> insertion = new HashMap<>();
protected boolean disableInsertion = false; protected boolean disableInsertion = false;
public abstract SELF self(); public abstract SELF self();
@@ -139,6 +155,17 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
return self(); return self();
} }
/**
* Insert the specific contents by the id.
*
* @param id the id of the insertion text
* @return the current {@link ContentHandler} instance
*/
public SELF insert(@NotNull String id) {
this.insertion.put(id, null);
return self();
}
/** /**
* Insert the specific contents by the id. * Insert the specific contents by the id.
* *
@@ -205,37 +232,50 @@ public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEI
} }
for (String line : contents.lines()) { for (String line : contents.lines()) {
Matcher matcher = INSERT_PATTERN.matcher(line); Matcher insertMatcher = INSERT_PATTERN.matcher(line);
if (!matcher.matches()) { if (insertMatcher.matches()) {
lineConsumer.accept(parse(receiver, line)); doInsert(insertMatcher, receiver, lineConsumer);
continue; continue;
} }
String id = matcher.group("id"); Matcher enableMatcher = ENABLE_PATTERN.matcher(line);
List<String> values = Optional.ofNullable(this.insertion.get(id)) if (enableMatcher.matches()) {
.map(f -> f.apply(receiver)) if (this.insertion.containsKey(enableMatcher.group("id"))) {
.orElse(null); lineConsumer.accept(parse(receiver, enableMatcher.group("content")));
if (values == null || values.isEmpty()) continue; }
continue;
String prefix = matcher.group("prefix");
String type = matcher.group("type");
boolean original = type.equals("@");
int offsetAbove = Optional.ofNullable(matcher.group("above"))
.map(Integer::parseInt).orElse(0);
int offsetDown = Optional.ofNullable(matcher.group("down"))
.map(Integer::parseInt).orElse(offsetAbove); // If offsetDown is not set, use offsetAbove
IntStream.range(0, Math.max(0, offsetAbove)).mapToObj(i -> "").forEach(lineConsumer);
String prefixContent = Optional.ofNullable(prefix).map(p -> parse(receiver, p)).orElse("");
if (original) {
values.stream().map(value -> prefixContent + value).forEach(lineConsumer);
} else {
values.stream().map(value -> prefixContent + parse(receiver, value)).forEach(lineConsumer);
} }
IntStream.range(0, Math.max(0, offsetDown)).mapToObj(i -> "").forEach(lineConsumer);
lineConsumer.accept(parse(receiver, line));
} }
} }
private void doInsert(Matcher matcher, @Nullable RECEIVER receiver,
@NotNull Consumer<String> lineConsumer) {
String id = matcher.group("id");
List<String> values = Optional.ofNullable(this.insertion.get(id))
.map(f -> f.apply(receiver))
.orElse(null);
if (values == null || values.isEmpty()) return; // No values to insert
String prefix = matcher.group("prefix");
String type = matcher.group("type");
boolean original = type.equals("@");
int offsetAbove = Optional.ofNullable(matcher.group("above"))
.map(Integer::parseInt).orElse(0);
int offsetDown = Optional.ofNullable(matcher.group("down"))
.map(Integer::parseInt).orElse(offsetAbove); // If offsetDown is not set, use offsetAbove
IntStream.range(0, Math.max(0, offsetAbove)).mapToObj(i -> "").forEach(lineConsumer);
String prefixContent = Optional.ofNullable(prefix).map(p -> parse(receiver, p)).orElse("");
if (original) {
values.stream().map(value -> prefixContent + value).forEach(lineConsumer);
} else {
values.stream().map(value -> prefixContent + parse(receiver, value)).forEach(lineConsumer);
}
IntStream.range(0, Math.max(0, offsetDown)).mapToObj(i -> "").forEach(lineConsumer);
}
public static String setPlaceholders(@NotNull String messages, public static String setPlaceholders(@NotNull String messages,
@NotNull Map<String, Object> placeholders) { @NotNull Map<String, Object> placeholders) {
if (messages.isEmpty()) return messages; if (messages.isEmpty()) return messages;
@@ -2,10 +2,12 @@ package cc.carm.lib.configuration.value.text.tests;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory; import cc.carm.lib.configuration.source.json.JSONConfigFactory;
import cc.carm.lib.configuration.value.text.tests.conf.AppMessages; import cc.carm.lib.configuration.value.text.tests.conf.AppMessages;
import org.junit.Test; import org.junit.Test;
import java.io.File;
public class ConfigTest { public class ConfigTest {
@@ -19,7 +21,7 @@ public class ConfigTest {
@Test @Test
public void test() { public void test() {
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/messages.yml").build(); ConfigurationHolder<?> holder = JSONConfigFactory.from(new File("target/messages.json")).build();
holder.initialize(AppMessages.class); holder.initialize(AppMessages.class);
@@ -20,7 +20,8 @@ public class ParseTest {
lines.add("#guidance#"); lines.add("#guidance#");
lines.add("{- }#websites#{0,1}"); lines.add("{- }#websites#{0,1}");
lines.add("Thanks for your reading!"); lines.add("Thanks for your reading!");
lines.add("?[click]");
lines.add("?[click]Click to see more!");
Map<String, List<String>> optional = new HashMap<>(); Map<String, List<String>> optional = new HashMap<>();
optional.put("guidance", Arrays.asList("To get more information for %(name), see:")); optional.put("guidance", Arrays.asList("To get more information for %(name), see:"));
@@ -35,6 +36,7 @@ public class ParseTest {
msg.placeholder("name", "Carm") msg.placeholder("name", "Carm")
.insert("guidance") .insert("guidance")
.insert("click")
.insert("websites", "Baidu", "Bilibili", "Google"); .insert("websites", "Baidu", "Bilibili", "Google");
System.out.println("----------------------------"); System.out.println("----------------------------");
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.8</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
+2 -2
View File
@@ -15,7 +15,7 @@
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>4.0.8</version> <version>4.0.11</version>
<modules> <modules>
<module>core</module> <module>core</module>
<module>features/section</module> <module>features/section</module>
@@ -26,7 +26,7 @@
<module>providers/yaml</module> <module>providers/yaml</module>
<module>providers/gson</module> <module>providers/gson</module>
<!-- <module>providers/hocon</module>--> <module>providers/hocon</module>
<module>providers/sql</module> <module>providers/sql</module>
<module>providers/mongodb</module> <module>providers/mongodb</module>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.8</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
+2 -2
View File
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>4.0.0</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -1,36 +0,0 @@
package cc.carm.lib.configuration;
import cc.carm.lib.configuration.hocon.HOCONFileConfigProvider;
import java.io.File;
import java.io.IOException;
public class EasyConfiguration {
private EasyConfiguration() {
}
public static HOCONFileConfigProvider from(File file, String source) {
HOCONFileConfigProvider provider = new HOCONFileConfigProvider(file);
try {
provider.initializeFile(source);
provider.initializeConfig();
} catch (IOException e) {
e.printStackTrace();
}
return provider;
}
public static HOCONFileConfigProvider from(File file) {
return from(file, file.getName());
}
public static HOCONFileConfigProvider from(String fileName) {
return from(fileName, fileName);
}
public static HOCONFileConfigProvider from(String fileName, String source) {
return from(new File(fileName), source);
}
}
@@ -1,116 +0,0 @@
package cc.carm.lib.configuration.hocon;
import cc.carm.lib.configuration.hocon.util.HOCONUtils;
import com.typesafe.config.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class HOCONConfigWrapper implements ConfigurationWrapper<Map<String, Object>> {
private static final char SEPARATOR = '.';
protected final Map<String, Object> data;
public HOCONConfigWrapper(ConfigObject config) {
this.data = new LinkedHashMap<>();
config.forEach((key, value) -> {
Config cfg = config.toConfig();
ConfigValue cv = cfg.getValue(key);
if (cv.valueType() == ConfigValueType.OBJECT) {
HOCONConfigWrapper.this.data.put(key, new HOCONConfigWrapper((ConfigObject) cv));
} else {
HOCONConfigWrapper.this.data.put(key, value.unwrapped());
}
});
}
@Override
public @NotNull Map<String, Object> getSource() {
return this.data;
}
@Override
public @NotNull Set<String> getKeys(boolean deep) {
return this.getValues(deep).keySet();
}
@Override
public @NotNull Map<String, Object> getValues(boolean deep) {
return HOCONUtils.getKeysFromObject(this, deep, "").stream().collect(
LinkedHashMap::new,
(map, key) -> map.put(key, get(key)),
LinkedHashMap::putAll
);
}
@Override
public void set(@NotNull String path, @Nullable Object value) {
if (value instanceof Map) {
//noinspection unchecked
value = new HOCONConfigWrapper(ConfigFactory.parseMap((Map<String, ?>) value).root());
}
HOCONConfigWrapper section = HOCONUtils.getObjectOn(this, path, SEPARATOR);
String simplePath = HOCONUtils.getSimplePath(path, SEPARATOR);
if (value == null) {
section.data.remove(simplePath);
} else {
section.setDirect(simplePath, value);
}
}
/**
* 只能设置当前路径下的内容
* 避免环回
*
* @param path 路径
*/
public void setDirect(@NotNull String path, @Nullable Object value) {
this.data.put(path, value);
}
@Override
public boolean contains(@NotNull String path) {
return this.get(path) != null;
}
@Override
public @Nullable Object get(@NotNull String path) {
HOCONConfigWrapper section = HOCONUtils.getObjectOn(this, path, SEPARATOR);
return section.getDirect(HOCONUtils.getSimplePath(path, SEPARATOR));
}
/**
* 只能获取当前路径下的内容
* 避免环回
*
* @param path 路径
*/
public Object getDirect(@NotNull String path) {
return this.data.get(path);
}
@Override
public boolean isList(@NotNull String path) {
return this.get(path) instanceof List<?>;
}
@Override
public @Nullable List<?> getList(@NotNull String path) {
Object val = this.get(path);
return (val instanceof List<?>) ? (List<?>) val : null;
}
@Override
public boolean isConfigurationSection(@NotNull String path) {
return this.get(path) instanceof HOCONConfigWrapper;
}
@Override
public @Nullable ConfigurationWrapper<Map<String, Object>> getConfigurationSection(@NotNull String path) {
Object val = get(path);
return (val instanceof HOCONConfigWrapper) ? (HOCONConfigWrapper) val : null;
}
}
@@ -1,105 +0,0 @@
package cc.carm.lib.configuration.hocon;
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;
import com.typesafe.config.*;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import java.util.Set;
public class HOCONFileConfigProvider extends FileConfigProvider<HOCONConfigWrapper> {
protected final @NotNull ConfigurationComments comments = new ConfigurationComments();
protected HOCONConfigWrapper configuration;
protected ConfigInitializer<HOCONFileConfigProvider> initializer;
public HOCONFileConfigProvider(@NotNull File file) {
super(file);
this.initializer = new ConfigInitializer<>(this);
}
public void initializeConfig() {
try {
this.configuration = new HOCONConfigWrapper(ConfigFactory.parseFile(this.file, ConfigParseOptions.defaults()
.setSyntax(ConfigSyntax.CONF)
.setAllowMissing(false)).root());
} catch (ConfigException e) {
e.printStackTrace();
}
}
@Override
public @NotNull HOCONConfigWrapper getConfiguration() {
return this.configuration;
}
@Override
public void save() throws IOException {
Files.write(this.file.toPath(), HOCONUtils.renderWithComment(configuration, comments::getHeaderComment).getBytes(StandardCharsets.UTF_8));
}
@Override
protected void onReload() throws ConfigException {
ConfigObject conf = ConfigFactory.parseFile(this.file, ConfigParseOptions.defaults()
.setSyntax(ConfigSyntax.CONF)
.setAllowMissing(false)).root();
this.configuration = new HOCONConfigWrapper(conf);
}
@Override
public @NotNull ConfigurationComments getComments() {
return this.comments;
}
@Override
public @NotNull ConfigInitializer<HOCONFileConfigProvider> getInitializer() {
return this.initializer;
}
public String serializeValue(@NotNull String key, @NotNull Object value) {
// 带有 key=value 的新空对象
return ConfigFactory.empty()
.withValue(key, ConfigValueFactory.fromAnyRef(value))
.root().render();
}
public @NotNull Set<String> getKeys() {
return getKeys(null, true);
}
@Contract("null,_->!null")
public @Nullable Set<String> getKeys(@Nullable String sectionKey, boolean deep) {
if (sectionKey == null) { // 当前路径
return HOCONUtils.getKeysFromObject(this.configuration, deep, "");
}
HOCONConfigWrapper section;
try {
// 获取目标字段所在路径
section = (HOCONConfigWrapper) this.configuration.get(sectionKey);
} catch (ClassCastException e) {
// 值和类型不匹配
throw new HOCONGetValueException(e);
}
if (section == null) {
return null;
}
return HOCONUtils.getKeysFromObject(section, deep, "");
}
public @Nullable Object getValue(@NotNull String key) {
return this.configuration.get(key);
}
public @Nullable List<String> getHeaderComments(@Nullable String key) {
return this.comments.getHeaderComment(key);
}
}
@@ -1,19 +0,0 @@
package cc.carm.lib.configuration.hocon.exception;
public class HOCONGetValueException extends RuntimeException {
public HOCONGetValueException() {
super();
}
public HOCONGetValueException(String message) {
super(message);
}
public HOCONGetValueException(String message, Throwable cause) {
super(message, cause);
}
public HOCONGetValueException(Throwable cause) {
super(cause);
}
}
@@ -1,110 +0,0 @@
package cc.carm.lib.configuration.hocon.util;
import cc.carm.lib.configuration.hocon.HOCONConfigWrapper;
import cc.carm.lib.configuration.hocon.exception.HOCONGetValueException;
import com.typesafe.config.*;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
public class HOCONUtils {
private HOCONUtils() {
}
public static String getSimplePath(String path, char separator) {
int index = path.lastIndexOf(separator);
return (index == -1) ? path : path.substring(index + 1);
}
public static HOCONConfigWrapper getObjectOn(@NotNull HOCONConfigWrapper parent, @NotNull String path, char separator) {
String currentPath = path;
HOCONConfigWrapper currentObject = parent;
int index;
while ((index = currentPath.indexOf(separator)) != -1) {
HOCONConfigWrapper previousObject = currentObject;
String pathName = currentPath.substring(0, index);
try {
currentObject = (HOCONConfigWrapper) previousObject.getDirect(pathName);
} catch (ClassCastException e) {
throw new HOCONGetValueException(e);
}
if (currentObject == null) {
currentObject = new HOCONConfigWrapper(ConfigFactory.empty().root());
previousObject.setDirect(pathName, currentObject);
}
currentPath = currentPath.substring(index + 1);
}
return currentObject;
}
/**
* 在 Object 中获取所有键
* 思路:在第一次执行时 prefix 应该是 ""
* 后续找到了更深层的键,将会变为 "deep."
* 下一次键名就是 "deep.key"
*
* @param parent Object
* @param deep 是否更深层获取
* @param prefix 当前 Object 键名前缀
* @return Object 中的所有键
*/
public static Set<String> getKeysFromObject(HOCONConfigWrapper parent, boolean deep, String prefix) {
return parent.getSource().entrySet().stream().collect(
LinkedHashSet::new,
(set, entry) -> {
Object value = entry.getValue();
if (value instanceof HOCONConfigWrapper && deep) {
set.addAll(HOCONUtils.getKeysFromObject((HOCONConfigWrapper) value, true, prefix + entry.getKey() + "."));
} else {
set.add(prefix + entry.getKey());
}
},
LinkedHashSet::addAll
);
}
/**
* 将 Object 保存为字符串
* 并使用注释器打上注释
*
* @param object Object
* @param commenter 注释器
* @return 保存的字符串
*/
public static @NotNull String renderWithComment(@NotNull HOCONConfigWrapper object, @NotNull Function<String, List<String>> commenter) {
return HOCONUtils.makeConfigWithComment(object, "", commenter).root().render(
ConfigRenderOptions.defaults()
.setJson(false)
.setOriginComments(false)
);
}
public static @NotNull Config makeConfigWithComment(@NotNull HOCONConfigWrapper object, @NotNull String prefix, @NotNull Function<String, List<String>> commenter) {
Config config = ConfigFactory.empty();
for (Map.Entry<String, Object> entry : object.getSource().entrySet()) {
String key = entry.getKey();
String fullKey = prefix + key;
Object value = entry.getValue();
ConfigValue result;
if (value instanceof Iterable) {
result = ConfigValueFactory.fromIterable((Iterable<?>) value);
} else if (value instanceof HOCONConfigWrapper) {
result = makeConfigWithComment((HOCONConfigWrapper) value, fullKey + ".", commenter).root();
} else {
result = ConfigValueFactory.fromAnyRef(value);
}
result = result.withOrigin(
ConfigOriginFactory.newSimple()
.withComments(commenter.apply(fullKey))
);
config = config.withValue(key, result);
}
return config;
}
}
@@ -0,0 +1,48 @@
package cc.carm.lib.configuration.source.hocon;
import cc.carm.lib.configuration.commentable.Commentable;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.file.FileConfigFactory;
import org.jetbrains.annotations.NotNull;
import java.io.File;
public class HOCONConfigFactory extends FileConfigFactory<HOCONSource, ConfigurationHolder<HOCONSource>, HOCONConfigFactory> {
public static HOCONConfigFactory from(@NotNull String path) {
return new HOCONConfigFactory(new File(path));
}
public static HOCONConfigFactory from(@NotNull File file) {
return new HOCONConfigFactory(file);
}
public static HOCONConfigFactory from(@NotNull File parent, @NotNull String configName) {
return new HOCONConfigFactory(new File(parent, configName));
}
public HOCONConfigFactory(@NotNull File file) {
super(file);
}
@Override
protected HOCONConfigFactory self() {
return this;
}
@Override
public @NotNull ConfigurationHolder<HOCONSource> build() {
File configFile = this.file;
String sourcePath = this.resourcePath;
Commentable.registerMeta(this.initializer); // Register commentable meta types
return new ConfigurationHolder<HOCONSource>(this.adapters, this.options, this.metadata, this.initializer) {
final @NotNull HOCONSource source = new HOCONSource(this, configFile, sourcePath);
@Override
public @NotNull HOCONSource config() {
return this.source;
}
};
}
}
@@ -1,69 +0,0 @@
package cc.carm.lib.configuration.source.hocon;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import com.typesafe.config.Config;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
import java.util.List;
import java.util.Map;
public class HOCONSection implements ConfigureSection {
protected final @NotNull HOCONSource source;
protected final @Nullable HOCONSection parent;
protected final @NotNull Config data;
public HOCONSection(@NotNull HOCONSource source, @Nullable HOCONSection parent,
@NotNull Config data) {
this.source = source;
this.parent = parent;
this.data = data;
}
public @NotNull Config data() {
return this.data;
}
@Override
public @NotNull HOCONSource source() {
return this.source;
}
@Override
public @Nullable HOCONSection parent() {
return this.parent;
}
@Override
public @NotNull @UnmodifiableView Map<String, Object> getValues(boolean deep) {
return data().root().unwrapped();
}
@Override
public void set(@NotNull String path, @Nullable Object value) {
}
@Override
public boolean contains(@NotNull String path) {
return data().hasPath(path);
}
@Override
public @Nullable List<?> getList(@NotNull String path) {
return data().getAnyRefList(path);
}
@Override
public @Nullable HOCONSection getSection(@NotNull String path) {
return data().getConfig(path) == null ? null : new HOCONSection(source, this, data().getConfig(path));
}
@Override
public @Nullable Object get(@NotNull String path) {
return data().getAnyRef(path);
}
}
@@ -1,44 +1,104 @@
package cc.carm.lib.configuration.source.hocon; package cc.carm.lib.configuration.source.hocon;
import cc.carm.lib.configuration.commentable.Commentable;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.section.ConfigureSource; import cc.carm.lib.configuration.source.file.FileConfigSource;
import com.typesafe.config.Config; import cc.carm.lib.configuration.source.section.SourcedSection;
import com.typesafe.config.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects; import java.io.File;
import java.util.*;
public class HOCONSource extends ConfigureSource<HOCONSection, Config, HOCONSource> { public class HOCONSource
extends FileConfigSource<SourcedSection, Map<String, Object>, HOCONSource> {
protected @Nullable SourcedSection rootSection;
protected HOCONSource(
@NotNull ConfigurationHolder<? extends HOCONSource> holder,
@NotNull File file, @Nullable String resourcePath
) {
super(holder, 0, file, resourcePath);
private HOCONSection rootSection; this.initialize();
}
protected HOCONSource(@NotNull ConfigurationHolder<? extends HOCONSource> holder, long lastUpdateMillis) { public void initialize() {
super(holder, lastUpdateMillis); try {
this.initializeFile();
this.onReload();
} catch (Exception e) {
//noinspection CallToPrintStackTrace
e.printStackTrace();
}
} }
@Override @Override
protected HOCONSource self() { protected @NotNull HOCONSource self() {
return this; return this;
} }
@Override @Override
public @NotNull Config original() { public @NotNull Map<String, Object> original() {
return section().data(); return this.section().data();
} }
@Override @Override
public @NotNull HOCONSection section() { public @NotNull SourcedSection section() {
return Objects.requireNonNull(rootSection, "RootSection is not initialized"); return Objects.requireNonNull(this.rootSection, "Root section is not initialized.");
}
public @NotNull String saveToString() {
// identity: 创建新的空 typesafe config
// accumulator: 将 Section 中的信息为 typesafe config 添加并返回
// combiner: 合并两个配置文件
Config config = this.getValues(true).entrySet().stream().reduce(
ConfigFactory.empty(),
(cfg, entry) -> {
String key = entry.getKey(); // 源数据 key
Object value = entry.getValue(); // 源数据 value
ConfigValue result; // 最终转换为 typesafe 的 ConfigValue 类型
if (value == null || value instanceof Boolean || value instanceof String || value instanceof Number) {
result = ConfigValueFactory.fromAnyRef(value); // 原始数据类型
} else if (value instanceof Iterator) {
result = ConfigValueFactory.fromIterable((Iterable<?>) value);
} else if (value instanceof Map) {
//noinspection unchecked
result = ConfigValueFactory.fromMap((Map<String, ?>) value);
} else {
result = ConfigValueFactory.fromAnyRef(String.valueOf(value));
}
List<String> headerComments = HOCONSource.this.getHeaderComments(key); // 获取其注释
result = result.withOrigin(result.origin().withComments(headerComments)); // 赋予其注释
return cfg.withValue(key, result); // 将其添加到根 config 中
},
Config::withFallback
);
return config.root().render(
ConfigRenderOptions.defaults()
.setJson(false)
.setOriginComments(false)
);
} }
@Override @Override
public void save() throws Exception { public void save() throws Exception {
this.fileWriter(w -> w.write(HOCONSource.this.saveToString()));
} }
@Override @Override
protected void onReload() throws Exception { protected void onReload() throws Exception {
this.rootSection = this.fileReadString(this::loadFromString);
} }
protected @NotNull SourcedSection loadFromString(@NotNull String data) {
ConfigObject config = ConfigFactory.parseString(data).root();
return SourcedSection.root(this, config.unwrapped());
}
public @Nullable List<String> getHeaderComments(@Nullable String key) {
return Commentable.getHeaderComments(holder(), key);
}
} }
@@ -1,18 +0,0 @@
package online.flowerinsnow.test.easyconfiguration;
public class HOCONTest {
// @Test
// public void onTest() {
// HOCONFileConfigProvider provider = EasyConfiguration.from(new File("target/hocon.conf"));
//
// ConfigurationTest.testDemo(provider);
// ConfigurationTest.testInner(provider);
//
// try {
// provider.save();
// provider.reload();
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
}
@@ -0,0 +1,38 @@
package sample;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.ConfigPath;
import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.value.standard.ConfiguredList;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import java.util.UUID;
@ConfigPath(root = true)
@HeaderComments("Configurations for sample")
public interface SampleConfig extends Configuration {
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
@HeaderComments("Server configurations") // Header comment
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class);
@HeaderComments({"[ UUID >-----------------------------------", "A lot of UUIDs"})
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
.parse(UUID::fromString).serialize(UUID::toString)
.defaults(
UUID.fromString("00000000-0000-0000-0000-000000000000"),
UUID.fromString("00000000-0000-0000-0000-000000000001")
).build();
@ConfigPath("info") // Custom path
interface INFO extends Configuration {
@HeaderComments("Configure your name!") // Header comment
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
@ConfigPath("how-old-are-you") // Custom path
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
}
}
@@ -0,0 +1,27 @@
package sample;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.hocon.HOCONConfigFactory;
import org.junit.Test;
public class SampleTest {
@Test
public void test() {
// 1. Make a configuration provider from a file.
ConfigurationHolder<?> holder = HOCONConfigFactory.from("target/config.conf")
.resourcePath("configs/sample.conf")
.build();
// 2. Initialize the configuration classes or instances.
holder.initialize(SampleConfig.class);
// 3. Enjoy using the configuration!
System.out.println("Enabled? -> " + SampleConfig.ENABLED.resolve());
SampleConfig.ENABLED.set(false);
System.out.println("And now? -> " + SampleConfig.ENABLED.resolve());
// p.s. Changes not save so enable value will still be true in the next run.
System.out.println("Your name is " + SampleConfig.INFO.NAME.resolve() + " (age=" + SampleConfig.INFO.AGE.resolve() + ")!");
}
}
@@ -0,0 +1,2 @@
version = 1.0
test-save = false
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.8</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -55,7 +55,7 @@ public class MongoSource extends ConfigureSource<SourcedSection, Map<String, Obj
@Override @Override
public void save() throws Exception { public void save() throws Exception {
Map<String, Object> data = this.rootSection.rawMap(); Map<String, Object> data = this.rootSection.asMap();
if (data.isEmpty()) return; // Skip saving if empty if (data.isEmpty()) return; // Skip saving if empty
if (data.containsKey("_id") && data.size() == 1) return; // Skip saving if only contains _id if (data.containsKey("_id") && data.size() == 1) return; // Skip saving if only contains _id
+1 -1
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.8</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -7,12 +7,17 @@ import cc.carm.lib.configuration.source.ConfigurationFactory;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.versioned.VersionedMetaTypes; import cc.carm.lib.configuration.versioned.VersionedMetaTypes;
import cc.carm.lib.easysql.api.SQLManager; import cc.carm.lib.easysql.api.SQLManager;
import cc.carm.lib.easysql.api.SQLTable;
import cc.carm.lib.easysql.api.builder.TableCreateBuilder;
import cc.carm.lib.easysql.api.enums.IndexType;
import cc.carm.lib.easysql.api.function.SQLHandler;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range; import org.jetbrains.annotations.Range;
import java.util.HashMap; import java.util.HashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
@@ -26,11 +31,43 @@ public class SQLConfigFactory extends ConfigurationFactory<SQLSource, Configurat
return from(() -> manager); return from(() -> manager);
} }
protected static final @NotNull Gson DEFAULT_GSON = new GsonBuilder()
.serializeNulls().disableHtmlEscaping().create();
protected static final @NotNull BiConsumer<String, TableCreateBuilder> DEFAULT_TABLE_SCHEMA = (tableName, builder) -> {
builder.addColumn("namespace", "VARCHAR(32) NOT NULL");
builder.addColumn("path", "VARCHAR(96) NOT NULL");
builder.addColumn("value", "TEXT");
builder.addColumn("inline_comment", "TEXT");
builder.addColumn("header_comments", "MEDIUMTEXT");
builder.addColumn("footer_comments", "MEDIUMTEXT");
builder.addColumn("type", "TINYINT NOT NULL DEFAULT 0");
builder.addColumn("version", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0");
builder.addColumn(
"create_time",
"TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP"
);
builder.addColumn(
"update_time",
"TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
);
builder.setIndex(
IndexType.PRIMARY_KEY, "pk_" + tableName.toLowerCase(),
"namespace", "path"
);
builder.setTableSettings("ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
};
protected @NotNull Supplier<SQLManager> managerSupplier; protected @NotNull Supplier<SQLManager> managerSupplier;
protected Supplier<Gson> gsonSupplier = () -> SQLSource.DEFAULT_GSON; protected Supplier<Gson> gsonSupplier = () -> DEFAULT_GSON;
protected HashMap<Integer, SQLValueResolver<?>> resolvers = new HashMap<>(SQLValueResolver.STANDARD_RESOLVERS); protected HashMap<Integer, SQLValueResolver<?>> resolvers = new HashMap<>(SQLValueResolver.STANDARD_RESOLVERS);
protected String tableName = "configs"; protected SQLTable tableName = SQLTable.of("config", (table) -> DEFAULT_TABLE_SCHEMA.accept("config", table));
protected String namespace = "default"; protected String namespace = "default";
public SQLConfigFactory(@NotNull Supplier<SQLManager> managerSupplier) { public SQLConfigFactory(@NotNull Supplier<SQLManager> managerSupplier) {
@@ -95,11 +132,19 @@ public class SQLConfigFactory extends ConfigurationFactory<SQLSource, Configurat
return resolver(id, SQLValueResolver.of(type, parser, serializer)); return resolver(id, SQLValueResolver.of(type, parser, serializer));
} }
public SQLConfigFactory tableName(@NotNull String tableName) { public SQLConfigFactory table(@NotNull SQLTable table) {
this.tableName = tableName; this.tableName = table;
return self(); return self();
} }
public SQLConfigFactory table(@NotNull String tableName, @NotNull SQLHandler<TableCreateBuilder> builder) {
return table(SQLTable.of(tableName, builder));
}
public SQLConfigFactory tableName(@NotNull String tableName) {
return table(tableName, table -> DEFAULT_TABLE_SCHEMA.accept(tableName, table));
}
public SQLConfigFactory namespace(@NotNull String namespace) { public SQLConfigFactory namespace(@NotNull String namespace) {
this.namespace = namespace; this.namespace = namespace;
return self(); return self();
@@ -9,9 +9,7 @@ import cc.carm.lib.configuration.versioned.VersionedMetaTypes;
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;
import cc.carm.lib.easysql.api.SQLTable; import cc.carm.lib.easysql.api.SQLTable;
import cc.carm.lib.easysql.api.enums.IndexType;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -21,9 +19,6 @@ import java.util.*;
public class SQLSource extends ConfigureSource<SourcedSection, Map<String, Object>, SQLSource> { public class SQLSource extends ConfigureSource<SourcedSection, Map<String, Object>, SQLSource> {
protected static final @NotNull Gson DEFAULT_GSON = new GsonBuilder()
.serializeNulls().disableHtmlEscaping().create();
protected final @NotNull Gson gson; protected final @NotNull Gson gson;
protected final @NotNull SQLManager sqlManager; protected final @NotNull SQLManager sqlManager;
protected final @NotNull String namespace; protected final @NotNull String namespace;
@@ -35,35 +30,13 @@ public class SQLSource extends ConfigureSource<SourcedSection, Map<String, Objec
public SQLSource(@NotNull ConfigurationHolder<? extends SQLSource> holder, long lastUpdateMillis, public SQLSource(@NotNull ConfigurationHolder<? extends SQLSource> holder, long lastUpdateMillis,
@NotNull Gson gson, @NotNull SQLManager sqlManager, @NotNull Gson gson, @NotNull SQLManager sqlManager,
@NotNull Map<Integer, SQLValueResolver<?>> resolvers, @NotNull Map<Integer, SQLValueResolver<?>> resolvers,
@NotNull String tableName, @NotNull String namespace) { @NotNull SQLTable table, @NotNull String namespace) {
super(holder, lastUpdateMillis); super(holder, lastUpdateMillis);
this.gson = gson; this.gson = gson;
this.sqlManager = sqlManager; this.sqlManager = sqlManager;
this.resolvers = resolvers; this.resolvers = resolvers;
this.namespace = namespace; this.namespace = namespace;
this.table = SQLTable.of(tableName, builder -> { this.table = table;
builder.addColumn("namespace", "VARCHAR(32) NOT NULL");
builder.addColumn("path", "VARCHAR(96) NOT NULL");
builder.addColumn("value", "TEXT");
builder.addColumn("inline_comment", "TEXT");
builder.addColumn("header_comments", "MEDIUMTEXT");
builder.addColumn("footer_comments", "MEDIUMTEXT");
builder.addColumn("type", "TINYINT NOT NULL DEFAULT 0");
builder.addColumn("version", "MEDIUMINT UNSIGNED NOT NULL DEFAULT 0");
builder.addColumn("create_time", "TIMESTAMP NOT NULL");
builder.addColumn("update_time", "TIMESTAMP NOT NULL");
builder.setIndex(
IndexType.PRIMARY_KEY, "pk_" + tableName.toLowerCase(),
"namespace", "path"
);
builder.setTableSettings("ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
});
try { try {
this.table.create(this.sqlManager); this.table.create(this.sqlManager);
@@ -109,12 +82,12 @@ public class SQLSource extends ConfigureSource<SourcedSection, Map<String, Objec
@Nullable Object value = section.get(path); @Nullable Object value = section.get(path);
if (value instanceof SourcedSection) { if (value instanceof SourcedSection) {
value = ((SourcedSection) value).rawMap(); value = ((SourcedSection) value).asMap();
} else if (value instanceof List<?>) { } else if (value instanceof List<?>) {
List<Object> list = new ArrayList<>(); List<Object> list = new ArrayList<>();
for (Object obj : (List<?>) value) { for (Object obj : (List<?>) value) {
if (obj instanceof SourcedSection) { if (obj instanceof SourcedSection) {
list.add(((SourcedSection) obj).rawMap()); list.add(((SourcedSection) obj).asMap());
} else { } else {
list.add(obj); list.add(obj);
} }
+2 -2
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>4.0.8</version> <version>4.0.11</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -14,7 +14,7 @@
<maven.compiler.target>${project.jdk.version}</maven.compiler.target> <maven.compiler.target>${project.jdk.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<deps.yamlcommentwriter.version>1.2.0</deps.yamlcommentwriter.version> <deps.yamlcommentwriter.version>1.2.1</deps.yamlcommentwriter.version>
</properties> </properties>
<artifactId>easyconfiguration-yaml</artifactId> <artifactId>easyconfiguration-yaml</artifactId>
@@ -1,5 +1,6 @@
package yaml.test; package yaml.test;
import cc.carm.lib.configuration.commentable.Commentable;
import cc.carm.lib.configuration.commentable.CommentableMeta; import cc.carm.lib.configuration.commentable.CommentableMeta;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest; import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.ConfigurationHolder; import cc.carm.lib.configuration.source.ConfigurationHolder;
@@ -21,7 +22,6 @@ public class YamlTests {
ConfigurationTest.testDemo(holder); ConfigurationTest.testDemo(holder);
ConfigurationTest.testInner(holder); ConfigurationTest.testInner(holder);
Map<String, List<String>> headers = holder.extractMetadata(CommentableMeta.HEADER); Map<String, List<String>> headers = holder.extractMetadata(CommentableMeta.HEADER);
System.out.println("Header comments: "); System.out.println("Header comments: ");
headers.forEach((k, v) -> { headers.forEach((k, v) -> {