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

Merge branch '4.0.0'

# Conflicts:
#	providers/yaml/pom.xml
This commit is contained in:
2025-02-13 04:49:17 +08:00
149 changed files with 4720 additions and 3856 deletions
+107
View File
@@ -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"
}
}
}
}
```
+74
View File
@@ -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>
@@ -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"
}