mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 10:38:19 +08:00
Merge branch '4.0.0'
# Conflicts: # providers/yaml/pom.xml
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
# EasyConfiguration-JSON
|
||||
|
||||
JSON file-based implementation, compatible with all Java environments.
|
||||
|
||||
**Remember that JSON does not support file comments.**
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Maven Dependency
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!-- Using Maven Central Repository for secure and stable updates, though synchronization might be needed. -->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
|
||||
<id>EasyConfiguration</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
</project>
|
||||
```
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-json</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
### Gradle Dependency
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// Using Maven Central Repository for secure and stable updates, though synchronization might be needed.
|
||||
mavenCentral()
|
||||
|
||||
// Using GitHub dependencies for real-time updates, configuration required (recommended).
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
|
||||
}
|
||||
```
|
||||
|
||||
## Example file format
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1.0,
|
||||
"test-number": 3185496340759645184,
|
||||
"test-enum": "DAYS",
|
||||
"user": {
|
||||
"name": "774b3",
|
||||
"info": {
|
||||
"uuid": "f890b050-d3c5-4a32-a8b0-8a421ec2d4cc"
|
||||
}
|
||||
},
|
||||
"sub": {
|
||||
"that": {
|
||||
"operators": []
|
||||
}
|
||||
},
|
||||
"uuid-value": "a20c2eb2-e36b-40d7-a1ba-57826e3588c2",
|
||||
"users": {
|
||||
"1": "561f5142-8d59-4e50-855d-18638f3cfca8",
|
||||
"2": "629fadab-c625-4678-85d2-cc73cb4aa3b7",
|
||||
"3": "e29d1fb8-d8bd-4c2a-8ac0-4aaee77196dc",
|
||||
"4": "8ff8ab49-7c34-44c0-9edd-203a9d44f309",
|
||||
"5": "3c09dbff-ca37-468a-8c47-e8e52f837a54"
|
||||
},
|
||||
"inner": {
|
||||
"inner-value": 49.831712577873375
|
||||
},
|
||||
"class-value": 1.0,
|
||||
"test": {
|
||||
"user": {
|
||||
"name": "Carm",
|
||||
"info": {
|
||||
"uuid": "c3881d54-3d77-46ca-b031-2962b8b89141"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>3.9.1</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
<artifactId>easyconfiguration-gson</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-feature-file</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-demo</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.12.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package cc.carm.lib.configuration.source.json;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.file.FileConfigFactory;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class JSONConfigFactory extends FileConfigFactory<JSONSource, ConfigurationHolder<JSONSource>, JSONConfigFactory> {
|
||||
|
||||
public static JSONConfigFactory from(@NotNull File file) {
|
||||
return new JSONConfigFactory(file);
|
||||
}
|
||||
|
||||
public static JSONConfigFactory from(@NotNull File parent, @NotNull String configName) {
|
||||
return new JSONConfigFactory(new File(parent, configName));
|
||||
}
|
||||
|
||||
protected Supplier<Gson> gsonSupplier = () -> JSONSource.DEFAULT_GSON;
|
||||
|
||||
public JSONConfigFactory(@NotNull File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSONConfigFactory self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public JSONConfigFactory gson(@NotNull Supplier<Gson> gsonSupplier) {
|
||||
this.gsonSupplier = gsonSupplier;
|
||||
return self();
|
||||
}
|
||||
|
||||
public JSONConfigFactory gson(@NotNull Gson gson) {
|
||||
return gson(() -> gson);
|
||||
}
|
||||
|
||||
public JSONConfigFactory gson(@NotNull Consumer<GsonBuilder> builder) {
|
||||
return gson(() -> {
|
||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||
builder.accept(gsonBuilder);
|
||||
return gsonBuilder.create();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigurationHolder<JSONSource> build() {
|
||||
Gson gson = gsonSupplier.get();
|
||||
if (gson == null) throw new NullPointerException("No Gson instance provided.");
|
||||
|
||||
File configFile = this.file;
|
||||
String sourcePath = this.resourcePath;
|
||||
|
||||
return new ConfigurationHolder<JSONSource>(this.adapters, this.options, new ConcurrentHashMap<>(), this.initializer) {
|
||||
final JSONSource source = new JSONSource(this, 0, configFile, sourcePath, gson);
|
||||
|
||||
@Override
|
||||
public @NotNull JSONSource config() {
|
||||
return source;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package cc.carm.lib.configuration.source.json;
|
||||
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class JSONSection implements ConfigureSection {
|
||||
|
||||
protected final @NotNull ConfigureSource<? extends JSONSection, ?, ?> source;
|
||||
protected final @NotNull Map<String, Object> data;
|
||||
protected final @Nullable JSONSection parent;
|
||||
|
||||
public JSONSection(@NotNull ConfigureSource<? extends JSONSection, ?, ?> source,
|
||||
@NotNull Map<?, ?> data, @Nullable JSONSection parent) {
|
||||
this.source = source;
|
||||
this.parent = parent;
|
||||
this.data = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<?, ?> entry : data.entrySet()) {
|
||||
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
|
||||
|
||||
if (entry.getValue() instanceof Map) {
|
||||
this.data.put(key, new JSONSection(source, (Map<?, ?>) entry.getValue(), this));
|
||||
} else if (entry.getValue() instanceof List) {
|
||||
List<Object> list = new ArrayList<>();
|
||||
for (Object obj : (List<?>) entry.getValue()) {
|
||||
if (obj instanceof Map) {
|
||||
list.add(new JSONSection(source, (Map<?, ?>) obj, this));
|
||||
} else {
|
||||
list.add(obj);
|
||||
}
|
||||
}
|
||||
this.data.put(key, list);
|
||||
} else {
|
||||
this.data.put(key, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigureSource<? extends JSONSection, ?, ?> source() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
public @NotNull Map<String, Object> data() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
public @Nullable JSONSection parent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
if (deep) {
|
||||
Map<String, Object> values = new LinkedHashMap<>();
|
||||
mapChildrenValues(values, this, null, true);
|
||||
return Collections.unmodifiableMap(values);
|
||||
} else return Collections.unmodifiableMap(data());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
if (value instanceof Map) {
|
||||
value = new JSONSection(source(), (Map<?, ?>) value, this);
|
||||
}
|
||||
|
||||
JSONSection section = getSectionFor(path);
|
||||
if (section == this) {
|
||||
if (value == null) {
|
||||
this.data.remove(path);
|
||||
} else {
|
||||
this.data.put(path, value);
|
||||
}
|
||||
} else {
|
||||
section.set(getChild(path), value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path) {
|
||||
return get(path) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
JSONSection section = getSectionFor(path);
|
||||
return section == this ? data.get(path) : section.get(getChild(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList(@NotNull String path) {
|
||||
return get(path) instanceof List<?>;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<?> getList(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof List<?>) ? (List<?>) val : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSection(@NotNull String path) {
|
||||
return get(path) instanceof JSONSection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigureSection getSection(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof ConfigureSection) ? (ConfigureSection) val : null;
|
||||
}
|
||||
|
||||
private JSONSection getSectionFor(String path) {
|
||||
int index = path.indexOf(separator());
|
||||
if (index == -1) return this;
|
||||
|
||||
String root = path.substring(0, index);
|
||||
Object section = this.data.get(root);
|
||||
if (section == null) {
|
||||
section = new JSONSection(source(), new LinkedHashMap<>(), this);
|
||||
this.data.put(root, section);
|
||||
}
|
||||
|
||||
return (JSONSection) section;
|
||||
}
|
||||
|
||||
private String getChild(String path) {
|
||||
int index = path.indexOf(separator());
|
||||
return (index == -1) ? path : path.substring(index + 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map the values of the children of the section to the output map.
|
||||
*
|
||||
* @param output The map to map the values to
|
||||
* @param section The section to map the values from
|
||||
* @param parent The parent path
|
||||
* @param deep If the mapping should be deep
|
||||
* @author md_5, sk89q
|
||||
*/
|
||||
protected void mapChildrenValues(@NotNull Map<String, Object> output, @NotNull JSONSection section,
|
||||
@Nullable String parent, boolean deep) {
|
||||
for (Map.Entry<String, Object> entry : section.data().entrySet()) {
|
||||
String path = (parent == null ? "" : parent + separator()) + entry.getKey();
|
||||
output.remove(path);
|
||||
output.put(path, entry.getValue());
|
||||
if (deep && entry.getValue() instanceof JSONSection) {
|
||||
this.mapChildrenValues(output, (JSONSection) entry.getValue(), path, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package cc.carm.lib.configuration.source.json;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.file.FileConfigSource;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
public class JSONSource extends FileConfigSource<JSONSection, Map<?, ?>, JSONSource> {
|
||||
|
||||
public static final @NotNull Gson DEFAULT_GSON = new GsonBuilder()
|
||||
.serializeNulls().disableHtmlEscaping().setPrettyPrinting()
|
||||
.registerTypeAdapter(
|
||||
JSONSection.class,
|
||||
(JsonSerializer<JSONSection>) (src, typeOfSrc, context) -> context.serialize(src.data)
|
||||
).create();
|
||||
|
||||
protected final @NotNull Gson gson;
|
||||
protected @Nullable JSONSection rootSection;
|
||||
|
||||
protected JSONSource(@NotNull ConfigurationHolder<? extends JSONSource> holder, long lastUpdateMillis,
|
||||
@NotNull File file, @Nullable String resourcePath) {
|
||||
this(holder, lastUpdateMillis, file, resourcePath, DEFAULT_GSON);
|
||||
}
|
||||
|
||||
protected JSONSource(@NotNull ConfigurationHolder<? extends JSONSource> holder, long lastUpdateMillis,
|
||||
@NotNull File file, @Nullable String resourcePath, @NotNull Gson gson) {
|
||||
super(holder, lastUpdateMillis, file, resourcePath);
|
||||
this.gson = gson;
|
||||
initialize();
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
try {
|
||||
initializeFile();
|
||||
onReload();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONSource self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigureSource<?, ?, ?> source() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<?, ?> original() {
|
||||
return section().data();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull JSONSection section() {
|
||||
return Objects.requireNonNull(this.rootSection, "Section is not initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws Exception {
|
||||
fileWriter(writer -> gson.toJson(original(), writer));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReload() throws Exception {
|
||||
Map<?, ?> data = fileReader(reader -> gson.fromJson(reader, LinkedHashMap.class));
|
||||
if (data == null) data = new LinkedHashMap<>();
|
||||
this.rootSection = new JSONSection(this, data, null);
|
||||
this.lastUpdateMillis = System.currentTimeMillis(); // 更新时间
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package config;
|
||||
|
||||
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class JSONConfigTest {
|
||||
|
||||
protected final ConfigurationHolder<?> holder = JSONConfigFactory
|
||||
.from(new File("target"), "config.json")
|
||||
.resourcePath("example.json")
|
||||
.build();
|
||||
|
||||
@Test
|
||||
public void onTest() {
|
||||
|
||||
ConfigValue<Boolean> EXAMPLE = ConfiguredValue.of(false);
|
||||
EXAMPLE.initialize(this.holder, "example");
|
||||
|
||||
System.out.println("Example: " + EXAMPLE.get());
|
||||
|
||||
ConfigurationTest.testDemo(this.holder);
|
||||
ConfigurationTest.testInner(this.holder);
|
||||
|
||||
ConfigurationTest.save(this.holder);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"example": "true"
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
# EasyConfiguration-HOCON
|
||||
|
||||
HOCON file-based implementation, compatible with all Java environments.
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Maven Dependency
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!-- Using Maven Central Repository for secure and stable updates, though synchronization might be needed. -->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
|
||||
<id>EasyConfiguration</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
</project>
|
||||
```
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-hocon</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
### Gradle Dependency
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// Using Maven Central Repository for secure and stable updates, though synchronization might be needed.
|
||||
mavenCentral()
|
||||
|
||||
// Using GitHub dependencies for real-time updates, configuration required (recommended).
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
api "cc.carm.lib:easyconfiguration-hocon:[LATEST RELEASE]"
|
||||
}
|
||||
```
|
||||
|
||||
## Example file format
|
||||
|
||||
```hocon
|
||||
class-value=1.0
|
||||
# Inner Test
|
||||
inner {
|
||||
inner-value=72.0043567836829
|
||||
}
|
||||
sub {
|
||||
that {
|
||||
operators=[]
|
||||
}
|
||||
}
|
||||
test {
|
||||
# Section类型数据测试
|
||||
user {
|
||||
info {
|
||||
uuid="8aba6166-1dc3-476d-8eb6-8957434c05ba"
|
||||
}
|
||||
name=Carm
|
||||
}
|
||||
}
|
||||
test-enum=DAYS
|
||||
test-number=5555780951875134464
|
||||
# Section类型数据测试
|
||||
user {
|
||||
info {
|
||||
uuid="9ed3a8f3-ad2a-4a62-a720-5530f5d19b33"
|
||||
}
|
||||
name="9038c"
|
||||
}
|
||||
# [ID - UUID]对照表
|
||||
#
|
||||
# 用于测试Map类型的解析与序列化保存
|
||||
users {
|
||||
"1"="4bfe382e-7b9e-4dad-9314-d16ddeb99f34"
|
||||
"2"="6e587a1e-361e-43da-99ba-9de44db198dc"
|
||||
"3"=ce582c1c-d696-43d4-ab58-af40d000d656
|
||||
"4"="37b7eb1f-86b9-41c7-afa3-9ac9c75fef2c"
|
||||
"5"="2659c33a-3393-404d-960e-850fef3b23fd"
|
||||
}
|
||||
uuid-value="035e89e8-3fe8-45ed-a25d-eef0bbe8f73d"
|
||||
version=1.0
|
||||
|
||||
```
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<version>3.9.1</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<properties>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
<artifactId>easyconfiguration-hocon</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-demo</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.typesafe</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
<version>1.4.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,36 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package online.flowerinsnow.test.easyconfiguration;
|
||||
|
||||
import cc.carm.lib.configuration.EasyConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||
import cc.carm.lib.configuration.hocon.HOCONFileConfigProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
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,82 @@
|
||||
# EasyConfiguration-SQL
|
||||
|
||||
SQL database implementation, support for MySQL or MariaDB.
|
||||
|
||||
## Table schema
|
||||
```mysql
|
||||
CREATE TABLE IF NOT EXISTS conf
|
||||
(
|
||||
`namespace` VARCHAR(32) NOT NULL, # 命名空间 (代表其属于谁,类似于单个配置文件地址的概念)
|
||||
`path` VARCHAR(96) NOT NULL, # 配置路径 (ConfigPath)
|
||||
`type` TINYINT UNSIGNED NOT NULL DEFAULT 0, # 数据类型 (Integer/Byte/List/Map/...)
|
||||
`value` MEDIUMTEXT, # 配置项的值 (可能为JSON格式)
|
||||
`usage` TEXT, # 配置项的用法,本质是行内注释
|
||||
`descriptions` MEDIUMTEXT, # 配置项的描述,本质是顶部注释
|
||||
`version` INT UNSIGNED NOT NULL DEFAULT 0, # 配置项的版本
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, # 创建时间
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`namespace`, `path`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4;
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Maven Dependency
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!-- Using Maven Central Repository for secure and stable updates, though synchronization might be needed. -->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
|
||||
<id>EasyConfiguration</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
</project>
|
||||
```
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-sql</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
### Gradle Dependency
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// Using Maven Central Repository for secure and stable updates, though synchronization might be needed.
|
||||
mavenCentral()
|
||||
|
||||
// Using GitHub dependencies for real-time updates, configuration required (recommended).
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
api "cc.carm.lib:easyconfiguration-sql:[LATEST RELEASE]"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>3.9.1</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
|
||||
<log4j.version>2.24.3</log4j.version>
|
||||
</properties>
|
||||
<artifactId>easyconfiguration-sql</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.12.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easysql-api</artifactId>
|
||||
<version>0.4.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easysql-beecp</artifactId>
|
||||
<version>0.4.7</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-demo</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,26 @@
|
||||
package cc.carm.lib.configuration;
|
||||
|
||||
import cc.carm.lib.configuration.sql.SQLConfigProvider;
|
||||
import cc.carm.lib.easysql.api.SQLManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EasyConfiguration {
|
||||
|
||||
private EasyConfiguration() {
|
||||
}
|
||||
|
||||
public static SQLConfigProvider from(@NotNull SQLManager sqlManager, @NotNull String tableName, @NotNull String namespace) {
|
||||
SQLConfigProvider provider = new SQLConfigProvider(sqlManager, tableName, namespace);
|
||||
try {
|
||||
provider.initializeConfig();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
public static SQLConfigProvider from(@NotNull SQLManager sqlManager, @NotNull String tableName) {
|
||||
return from(sqlManager, tableName, "base");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package cc.carm.lib.configuration.sql;
|
||||
|
||||
import cc.carm.lib.configuration.source.comment.ConfigurationComments;
|
||||
import cc.carm.lib.easysql.api.SQLManager;
|
||||
import cc.carm.lib.easysql.api.SQLQuery;
|
||||
import cc.carm.lib.easysql.api.SQLTable;
|
||||
import cc.carm.lib.easysql.api.enums.IndexType;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.util.*;
|
||||
|
||||
public class SQLConfigProvider extends ConfigurationProvider<SQLConfigWrapper> {
|
||||
|
||||
public static Gson GSON = new GsonBuilder().serializeNulls().disableHtmlEscaping().setPrettyPrinting().create();
|
||||
|
||||
protected final @NotNull SQLManager sqlManager;
|
||||
protected final @NotNull SQLTable table;
|
||||
protected final @NotNull String namespace;
|
||||
|
||||
protected ConfigInitializer<SQLConfigProvider> initializer;
|
||||
protected ConfigurationComments comments = new ConfigurationComments();
|
||||
protected SQLConfigWrapper rootConfiguration;
|
||||
|
||||
protected final @NotNull Set<String> updated = new HashSet<>();
|
||||
|
||||
public SQLConfigProvider(@NotNull SQLManager sqlManager, @NotNull String tableName, @NotNull String namespace) {
|
||||
this.sqlManager = sqlManager;
|
||||
this.table = SQLTable.of(tableName, builder -> {
|
||||
|
||||
builder.addColumn("namespace", "VARCHAR(32) NOT NULL");
|
||||
builder.addColumn("path", "VARCHAR(96) NOT NULL");
|
||||
|
||||
builder.addColumn("type", "TINYINT NOT NULL DEFAULT 0");
|
||||
builder.addColumn("value", "TEXT");
|
||||
|
||||
builder.addColumn("inline_comment", "TEXT");
|
||||
builder.addColumn("header_comments", "MEDIUMTEXT");
|
||||
|
||||
builder.addColumn("create_time", "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
builder.addColumn("update_time", "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
|
||||
builder.setIndex(
|
||||
IndexType.PRIMARY_KEY, "pk_" + tableName.toLowerCase(),
|
||||
"namespace", "path"
|
||||
);
|
||||
builder.setTableSettings("ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
||||
});
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull SQLConfigWrapper getConfiguration() {
|
||||
return rootConfiguration;
|
||||
}
|
||||
|
||||
public void initializeConfig() throws Exception {
|
||||
this.table.create(this.sqlManager);
|
||||
this.initializer = new ConfigInitializer<>(this);
|
||||
onReload();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReload() throws Exception {
|
||||
this.comments = new ConfigurationComments();
|
||||
LinkedHashMap<String, Object> values = new LinkedHashMap<>();
|
||||
|
||||
try (SQLQuery query = this.table.createQuery()
|
||||
.addCondition("namespace", namespace)
|
||||
.build().execute()) {
|
||||
ResultSet rs = query.getResultSet();
|
||||
while (rs.next()) {
|
||||
String path = rs.getString("path");
|
||||
int type = rs.getInt("type");
|
||||
try {
|
||||
SQLValueResolver<?> resolver = SQLValueTypes.get(type);
|
||||
if (resolver == null) throw new IllegalStateException("No resolver for type #" + type);
|
||||
String value = rs.getString("value");
|
||||
values.put(path, resolver.resolve(value));
|
||||
|
||||
loadInlineComment(path, rs.getString("inline_comment"));
|
||||
loadHeaderComment(path, rs.getString("header_comments"));
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.rootConfiguration = new SQLConfigWrapper(this, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws Exception {
|
||||
if (this.updated.isEmpty()) return;
|
||||
|
||||
Date date = new Date();
|
||||
List<Object[]> values = new ArrayList<>();
|
||||
List<String> deletes = new ArrayList<>();
|
||||
|
||||
for (String path : this.updated) {
|
||||
Object value = this.rootConfiguration.get(path);
|
||||
if (value == null) {
|
||||
deletes.add(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value instanceof SQLConfigWrapper) {
|
||||
value = getSourceMap(((SQLConfigWrapper) value).getSource());
|
||||
}
|
||||
|
||||
SQLValueResolver<?> type = SQLValueTypes.get(value.getClass());
|
||||
if (type != null) {
|
||||
values.add(new Object[]{
|
||||
this.namespace, path, date,
|
||||
type.getID(), type.serializeObject(value),
|
||||
getComments().getInlineComment(path),
|
||||
GSON.toJson(getComments().getHeaderComment(path))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.updated.clear();
|
||||
|
||||
this.table.createReplaceBatch()
|
||||
.setColumnNames("namespace", "path", "update_time", "type", "value", "inline_comment", "header_comments")
|
||||
.setAllParams(values)
|
||||
.execute();
|
||||
|
||||
for (String path : deletes) {
|
||||
this.table.createDelete()
|
||||
.addCondition("namespace", this.namespace)
|
||||
.addCondition("path", path)
|
||||
.build().execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigurationComments getComments() {
|
||||
return this.comments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigInitializer<? extends ConfigurationProvider<SQLConfigWrapper>> getInitializer() {
|
||||
return this.initializer;
|
||||
}
|
||||
|
||||
|
||||
protected void loadInlineComment(String path, String comment) {
|
||||
if (comment == null) return;
|
||||
comment = comment.trim();
|
||||
if (comment.isEmpty()) return;
|
||||
|
||||
this.comments.setInlineComment(path, comment);
|
||||
}
|
||||
|
||||
protected void loadHeaderComment(String path, String commentJson) {
|
||||
if (commentJson == null) return;
|
||||
commentJson = commentJson.trim();
|
||||
if (commentJson.isEmpty()) return;
|
||||
|
||||
List<String> headerComments = GSON.fromJson(commentJson, new TypeToken<List<String>>() {
|
||||
}.getType());
|
||||
if (headerComments == null || headerComments.isEmpty()) return;
|
||||
|
||||
this.comments.setHeaderComments(path, headerComments);
|
||||
}
|
||||
|
||||
protected static Map<String, Object> getSourceMap(Map<String, Object> map) {
|
||||
Map<String, Object> source = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
if (entry.getValue() instanceof SQLConfigWrapper) {
|
||||
source.put(entry.getKey(), getSourceMap(((SQLConfigWrapper) entry.getValue()).getSource()));
|
||||
} else {
|
||||
source.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package cc.carm.lib.configuration.sql;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* For SQL configs, that primary path will be directly mapped to the value.
|
||||
*
|
||||
* @author CarmJos
|
||||
*/
|
||||
public class SQLConfigWrapper implements ConfigurationWrapper<Map<String, Object>> {
|
||||
|
||||
private static final char SEPARATOR = '.';
|
||||
protected final @NotNull SQLConfigProvider provider;
|
||||
protected final @NotNull Map<String, Object> data;
|
||||
|
||||
SQLConfigWrapper(@NotNull SQLConfigProvider provider, @NotNull Map<?, ?> map) {
|
||||
this.provider = provider;
|
||||
this.data = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
|
||||
|
||||
if (entry.getValue() instanceof Map) {
|
||||
this.data.put(key, new SQLConfigWrapper(provider, (Map<?, ?>) entry.getValue()));
|
||||
} else {
|
||||
this.data.put(key, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getSource() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<String> getKeys(boolean deep) {
|
||||
return getValues(deep).keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
// Deep is not supported for SQL configs.
|
||||
return new LinkedHashMap<>(this.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
if (value instanceof Map) {
|
||||
value = new SQLConfigWrapper(this.provider, (Map<?, ?>) value);
|
||||
}
|
||||
|
||||
SQLConfigWrapper section = getSectionFor(path);
|
||||
if (section == this) {
|
||||
if (value == null) {
|
||||
this.data.remove(path);
|
||||
} else {
|
||||
this.data.put(path, value);
|
||||
}
|
||||
} else {
|
||||
section.set(getChild(path), value);
|
||||
}
|
||||
|
||||
this.provider.updated.add(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path) {
|
||||
return get(path) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
SQLConfigWrapper section = getSectionFor(path);
|
||||
return section == this ? data.get(path) : section.get(getChild(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList(@NotNull String path) {
|
||||
return get(path) instanceof List<?>;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<?> getList(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof List<?>) ? (List<?>) val : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurationSection(@NotNull String path) {
|
||||
return get(path) instanceof SQLConfigWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SQLConfigWrapper getConfigurationSection(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof SQLConfigWrapper) ? (SQLConfigWrapper) val : null;
|
||||
}
|
||||
|
||||
private SQLConfigWrapper getSectionFor(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
if (index == -1) return this;
|
||||
|
||||
String root = path.substring(0, index);
|
||||
Object section = this.data.computeIfAbsent(root, k -> new SQLConfigWrapper(this.provider, new LinkedHashMap<>()));
|
||||
|
||||
return (SQLConfigWrapper) section;
|
||||
}
|
||||
|
||||
private String getChild(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
return (index == -1) ? path : path.substring(index + 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package cc.carm.lib.configuration.sql;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SQLValueResolver<T> {
|
||||
|
||||
public static <V> SQLValueResolver<V> of(int id, @NotNull Class<V> clazz,
|
||||
@NotNull ConfigDataFunction<String, V> resolver) {
|
||||
return of(id, clazz, resolver, String::valueOf);
|
||||
}
|
||||
|
||||
public static <V> SQLValueResolver<V> of(int id, @NotNull Class<V> clazz,
|
||||
@NotNull ConfigDataFunction<String, V> resolver,
|
||||
@NotNull Function<V, String> serializer) {
|
||||
return new SQLValueResolver<>(id, clazz, resolver, serializer);
|
||||
}
|
||||
|
||||
protected final int id;
|
||||
protected final @NotNull Class<T> clazz;
|
||||
protected final @NotNull ConfigDataFunction<String, T> resolver;
|
||||
protected final @NotNull Function<T, String> serializer;
|
||||
|
||||
protected SQLValueResolver(int id, @NotNull Class<T> clazz,
|
||||
@NotNull ConfigDataFunction<String, T> resolver,
|
||||
@NotNull Function<T, String> serializer) {
|
||||
this.id = id;
|
||||
this.clazz = clazz;
|
||||
this.resolver = resolver;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public @NotNull Class<T> getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public boolean isTypeOf(@NotNull Class<?> clazz) {
|
||||
return this.clazz.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
public @NotNull T resolve(String data) throws Exception {
|
||||
return resolver.parse(data);
|
||||
}
|
||||
|
||||
public @Nullable String serialize(T value) {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public @Nullable String serializeObject(Object value) {
|
||||
return isTypeOf(value.getClass()) ? serialize(clazz.cast(value)) : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package cc.carm.lib.configuration.sql;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
interface SQLValueTypes {
|
||||
|
||||
SQLValueResolver<String> STRING = SQLValueResolver.of(0, String.class, ConfigDataFunction.identity());
|
||||
SQLValueResolver<Byte> BYTE = SQLValueResolver.of(1, Byte.class, Byte::parseByte);
|
||||
SQLValueResolver<Short> SHORT = SQLValueResolver.of(2, Short.class, Short::parseShort);
|
||||
SQLValueResolver<Integer> INTEGER = SQLValueResolver.of(3, Integer.class, Integer::parseInt);
|
||||
SQLValueResolver<Long> LONG = SQLValueResolver.of(4, Long.class, Long::parseLong);
|
||||
SQLValueResolver<Float> FLOAT = SQLValueResolver.of(5, Float.class, Float::parseFloat);
|
||||
SQLValueResolver<Double> DOUBLE = SQLValueResolver.of(6, Double.class, Double::parseDouble);
|
||||
SQLValueResolver<Boolean> BOOLEAN = SQLValueResolver.of(7, Boolean.class, Boolean::parseBoolean);
|
||||
SQLValueResolver<Character> CHAR = SQLValueResolver.of(8, Character.class, s -> s.charAt(0));
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
SQLValueResolver<List> LIST = SQLValueResolver.of(10, List.class,
|
||||
array -> SQLConfigProvider.GSON.fromJson(array, List.class),
|
||||
list -> SQLConfigProvider.GSON.toJson(list)
|
||||
);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
SQLValueResolver<Map> SECTION = SQLValueResolver.of(20, Map.class,
|
||||
section -> SQLConfigProvider.GSON.fromJson(section, LinkedHashMap.class),
|
||||
section -> SQLConfigProvider.GSON.toJson(section)
|
||||
);
|
||||
|
||||
static @NotNull SQLValueResolver<?>[] values() {
|
||||
return new SQLValueResolver<?>[]{
|
||||
STRING, BYTE, SHORT, INTEGER, LONG, FLOAT, DOUBLE, BOOLEAN, CHAR, LIST, SECTION
|
||||
};
|
||||
}
|
||||
|
||||
static SQLValueResolver<?> valueOf(int index) {
|
||||
return values()[index];
|
||||
}
|
||||
|
||||
static @Nullable SQLValueResolver<?> get(int id) {
|
||||
return Arrays.stream(values()).filter(v -> v.id == id).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
static @Nullable SQLValueResolver<?> get(Class<?> typeClazz) {
|
||||
return Arrays.stream(values()).filter(v -> v.isTypeOf(typeClazz)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package config;
|
||||
|
||||
import cc.carm.lib.configuration.EasyConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||
import cc.carm.lib.configuration.sql.SQLConfigProvider;
|
||||
import cc.carm.lib.easysql.EasySQL;
|
||||
import cc.carm.lib.easysql.api.SQLManager;
|
||||
import cc.carm.lib.easysql.beecp.BeeDataSourceConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SQLConfigTest {
|
||||
|
||||
@Test
|
||||
public void onTest() {
|
||||
// test();
|
||||
}
|
||||
|
||||
|
||||
public void test() {
|
||||
BeeDataSourceConfig config = new BeeDataSourceConfig();
|
||||
config.setDriverClassName("org.h2.Driver");
|
||||
config.setJdbcUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=MySQL;");
|
||||
SQLManager manager = EasySQL.createManager(config);
|
||||
manager.setDebugMode(true);
|
||||
|
||||
SQLConfigProvider provider = EasyConfiguration.from(manager, "conf_test", "TESTING");
|
||||
|
||||
ConfigurationTest.testDemo(provider);
|
||||
ConfigurationTest.testInner(provider);
|
||||
|
||||
ConfigurationTest.save(provider);
|
||||
|
||||
EasySQL.shutdownManager(manager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN" packages="config.SQLConfigTest">
|
||||
<Appenders>
|
||||
<console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
|
||||
</console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<filters>
|
||||
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<RegexFilter regex=".*\$\{[^}]*\}.*" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
</filters>
|
||||
<AppenderRef ref="File"/>
|
||||
<appender-ref ref="Console"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
# EasyConfiguration-YAML
|
||||
|
||||
YAML file-based implementation, compatible with all Java environments.
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Maven Dependency
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!-- Using Maven Central Repository for secure and stable updates, though synchronization might be needed. -->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
|
||||
<id>EasyConfiguration</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
</project>
|
||||
```
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-yaml</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
### Gradle Dependency
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// Using Maven Central Repository for secure and stable updates, though synchronization might be needed.
|
||||
mavenCentral()
|
||||
|
||||
// Using GitHub dependencies for real-time updates, configuration required (recommended).
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
||||
}
|
||||
```
|
||||
|
||||
## Example file format
|
||||
```yaml
|
||||
version: 1.0.0
|
||||
|
||||
test-save: false
|
||||
|
||||
test-number: 6161926779561752576
|
||||
|
||||
test-enum: DAYS
|
||||
|
||||
# Section类型数据测试
|
||||
user: # Section数据也支持InlineComment注释
|
||||
name: '35882'
|
||||
info:
|
||||
uuid: 554b79f1-7c39-4960-82d1-5514c9734417
|
||||
|
||||
uuid-value: 9a86663e-2fc7-4851-a423-c7e5d8e94a47 # This is an inline comment
|
||||
|
||||
sub:
|
||||
that:
|
||||
operators: []
|
||||
|
||||
# [ID - UUID]对照表
|
||||
|
||||
# 用于测试Map类型的解析与序列化保存
|
||||
users:
|
||||
'1': 1c055bdd-c9d1-4931-8270-3d162247f38a
|
||||
'2': 934e2b05-2417-424e-80fd-fe58c6725837
|
||||
'3': 442949a2-8345-4210-a87b-593d7168980e
|
||||
'4': 5c015453-4b5b-42e3-ad87-b9498f2dfeab
|
||||
'5': 8f9640e7-0fbd-4f73-b737-f0b707215e71
|
||||
|
||||
# Inner Test
|
||||
inner:
|
||||
inner-value: 51.223503560658166
|
||||
|
||||
class-value: 1.0
|
||||
|
||||
test:
|
||||
# Section类型数据测试
|
||||
user: # Section数据也支持InlineComment注释
|
||||
name: Carm
|
||||
info:
|
||||
uuid: 3d1ef2a0-a38b-44f3-b15f-8e3b22cb8cc6
|
||||
|
||||
# 以下内容用于测试序列化
|
||||
model-test:
|
||||
some-model:
|
||||
==: SomeModel
|
||||
num: 855
|
||||
name: 4f6b7
|
||||
any-model:
|
||||
==: AnyModel
|
||||
name: 63d05
|
||||
state: false
|
||||
models:
|
||||
- name: 481f3
|
||||
state: true
|
||||
- name: fcf3e
|
||||
state: false
|
||||
- name: '14e50'
|
||||
state: false
|
||||
model-map:
|
||||
a:
|
||||
name: 1fb9b
|
||||
state: false
|
||||
b:
|
||||
name: 5486f
|
||||
state: false
|
||||
```
|
||||
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>3.9.1</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
|
||||
<artifactId>easyconfiguration-yaml</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>yamlcommentwriter</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-feature-file</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>2.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-demo</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package cc.carm.lib.configuration.source.yaml;
|
||||
|
||||
public class YAMLConfigFactory {
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package cc.carm.lib.configuration.source.yaml;
|
||||
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnmodifiableView;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class YAMLSection implements ConfigureSection {
|
||||
|
||||
protected final @NotNull ConfigureSource<? extends YAMLSection, ?, ?> source;
|
||||
protected final @NotNull Map<String, Object> data;
|
||||
protected final @Nullable YAMLSection parent;
|
||||
|
||||
public YAMLSection(@NotNull ConfigureSource<? extends YAMLSection, ?, ?> source,
|
||||
@NotNull Map<String, Object> data, @Nullable YAMLSection parent) {
|
||||
this.source = source;
|
||||
this.data = data;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigureSource<?, ?, ?> source() {
|
||||
return this.source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable YAMLSection parent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull @UnmodifiableView Map<String, Object> getValues(boolean deep) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList(@NotNull String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<?> getList(@NotNull String path) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSection(@NotNull String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable YAMLSection getSection(@NotNull String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package cc.carm.lib.configuration.source.yaml;
|
||||
|
||||
public class YAMLSource {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package sample;
|
||||
|
||||
public class Sample {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 1. Make a configuration provider from a file.
|
||||
ConfigurationProvider<?> provider = EasyConfiguration.from("config.yml");
|
||||
// 2. Initialize the configuration classes or instances.
|
||||
provider.initialize(SampleConfig.class);
|
||||
// 3. Enjoy using the configuration!
|
||||
SampleConfig.ENABLED.set(false);
|
||||
System.out.println("Your name is " + SampleConfig.INFO.NAME.getNotNull() + " !");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package sample;
|
||||
|
||||
import cc.carm.lib.configuration.source.Configuration;
|
||||
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.annotation.InlineComment;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@HeaderComment("Configurations for sample")
|
||||
public interface SampleConfig extends Configuration {
|
||||
|
||||
@InlineComment("Enabled?") // Inline comment
|
||||
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
|
||||
|
||||
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
|
||||
.parseValue(UUID::fromString).serializeValue(UUID::toString)
|
||||
.defaults(
|
||||
UUID.fromString("00000000-0000-0000-0000-000000000000"),
|
||||
UUID.fromString("00000000-0000-0000-0000-000000000001")
|
||||
).build();
|
||||
|
||||
interface INFO extends Configuration {
|
||||
|
||||
@HeaderComment("Configure your name!") // Header comment
|
||||
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
|
||||
|
||||
@ConfigPath("year") // Custom path
|
||||
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
version: 1.0
|
||||
test-save: false
|
||||
Reference in New Issue
Block a user