mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 18:48:20 +08:00
Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 04eaf6606d | |||
| e558e93410 | |||
| e55fe3a8d5 | |||
| 8f075b99b5 | |||
| 09a0b3c373 | |||
| 4df4977733 | |||
| 6434feb980 | |||
| bc3e4b3e6f | |||
| c2a9e2254c | |||
| 251dd208af | |||
| afd1bbfc0f | |||
| a699f6c164 | |||
| 844cbfab53 | |||
| f74d5d29f9 | |||
| 6f28abebb9 | |||
| 842cd78ce3 | |||
| 11f1f36a15 | |||
| ee9f29ba30 | |||
| f5bccdaad5 | |||
| f5f70ff69b | |||
| 3e221740bc | |||
| 96d09be977 | |||
| 5d7c946db5 | |||
| 9e008ff4cd | |||
| df19da170b | |||
| 3473ef2247 | |||
| d543530305 | |||
| d81855697c | |||
| 5c16e98f30 | |||
| 3f1ffadeff | |||
| d8191b7c6d | |||
| ba66220f92 | |||
| 730d6d7e9c | |||
| 4523190cb0 | |||
| 2a49e2ee6b | |||
| 77b223b2cb | |||
| 8e7ac263e7 | |||
| 80f03ec501 | |||
| 1c002ae535 | |||
| a4659c5c9f | |||
| 1c27c2f881 | |||
| 476cb79e2f | |||
| bf716b06ae | |||
| 00170e6d77 | |||
| 1bac201427 | |||
| cd77639df5 | |||
| 5ae756929a | |||
| a9e80aecce | |||
| 477f0d11bc | |||
| 53ff38a76f | |||
| c32bea3864 | |||
| 15823bb076 | |||
| f303f56199 | |||
| 22f2674e7c | |||
| 9eb9c9e13d | |||
| 616314c7f0 | |||
| 05f504a347 | |||
| bf6ea97b99 | |||
| 9847399e56 | |||
| 18515d4a78 | |||
| 9f1fc5bf90 | |||
| e7847b2166 | |||
| 69cec281be | |||
| 28dc9b9e55 | |||
| c60ba074d9 | |||
| e88bf301cc |
@@ -40,7 +40,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
rm -rf docs
|
rm -rf docs
|
||||||
mkdir -vp docs
|
mkdir -vp docs
|
||||||
cp -vrf core/target/apidocs/* docs/
|
cp -vrf core/target/reports/apidocs/* docs/
|
||||||
cp -vrf .doc/JAVADOC-README.md docs/README.md
|
cp -vrf .doc/JAVADOC-README.md docs/README.md
|
||||||
|
|
||||||
- name: "Generate the Javadoc sitemap"
|
- name: "Generate the Javadoc sitemap"
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
/.idea/
|
.idea/
|
||||||
**/target/
|
**/target/
|
||||||
**.iml
|
**.iml
|
||||||
@@ -49,41 +49,58 @@ Check out all code demonstrations [HERE](demo/src/main/java/cc/carm/lib/configur
|
|||||||
For more examples, see the [Development Guide](.doc/README.md).
|
For more examples, see the [Development Guide](.doc/README.md).
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@HeaderComment("Configurations for sample")
|
@ConfigPath(root = true)
|
||||||
interface SampleConfig extends Configuration {
|
@HeaderComments("Configurations for sample")
|
||||||
|
public interface SampleConfig extends Configuration {
|
||||||
|
|
||||||
@InlineComment("Enabled?") // Inline comment
|
@InlineComment("Enabled?") // Inline comment
|
||||||
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
|
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
|
||||||
|
|
||||||
|
@HeaderComments("Server configurations") // Header comment
|
||||||
|
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class);
|
||||||
|
|
||||||
|
@HeaderComments({"[ UUID >-----------------------------------", "A lot of UUIDs"})
|
||||||
|
@FooterComments("[ UUID >-----------------------------------")
|
||||||
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
|
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
|
||||||
.parseValue(UUID::fromString).serializeValue(UUID::toString)
|
.parse(UUID::fromString).serialize(UUID::toString)
|
||||||
.defaults(
|
.defaults(
|
||||||
UUID.fromString("00000000-0000-0000-0000-000000000000"),
|
UUID.fromString("00000000-0000-0000-0000-000000000000"),
|
||||||
UUID.fromString("00000000-0000-0000-0000-000000000001")
|
UUID.fromString("00000000-0000-0000-0000-000000000001")
|
||||||
).build();
|
).build();
|
||||||
|
|
||||||
|
@ConfigPath("info") // Custom path
|
||||||
interface INFO extends Configuration {
|
interface INFO extends Configuration {
|
||||||
|
|
||||||
@HeaderComment("Configure your name!") // Header comment
|
@HeaderComments("Configure your name!") // Header comment
|
||||||
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
|
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
|
||||||
|
|
||||||
@ConfigPath("year") // Custom path
|
@ConfigPath("how-old-are-you") // Custom path
|
||||||
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
|
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Sample {
|
public class Sample {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// 1. Make a configuration provider from a file.
|
// 1. Make a configuration provider from a file.
|
||||||
ConfigurationProvider<?> provider = EasyConfiguration.from("config.yml");
|
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/config.yml")
|
||||||
|
.resourcePath("configs/sample.yml")
|
||||||
|
.indent(4) // Optional: Set the indentation of the configuration file.
|
||||||
|
.build();
|
||||||
|
|
||||||
// 2. Initialize the configuration classes or instances.
|
// 2. Initialize the configuration classes or instances.
|
||||||
provider.initialize(SampleConfig.class);
|
holder.initialize(SampleConfig.class);
|
||||||
// 3. Enjoy using the configuration!
|
// 3. Enjoy using the configuration!
|
||||||
|
System.out.println("Enabled? -> " + SampleConfig.ENABLED.resolve());
|
||||||
SampleConfig.ENABLED.set(false);
|
SampleConfig.ENABLED.set(false);
|
||||||
System.out.println("Your name is " + SampleConfig.INFO.NAME.getNotNull() + " !");
|
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() + ")!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,14 +111,20 @@ public class Sample {
|
|||||||
|
|
||||||
enabled: true #Enabled?
|
enabled: true #Enabled?
|
||||||
|
|
||||||
|
# Server configurations
|
||||||
|
port:
|
||||||
|
|
||||||
|
# [ UUID >-----------------------------------
|
||||||
|
# A lot of UUIDs
|
||||||
uuids:
|
uuids:
|
||||||
- 00000000-0000-0000-0000-000000000000
|
- 00000000-0000-0000-0000-000000000000
|
||||||
- 00000000-0000-0000-0000-000000000001
|
- 00000000-0000-0000-0000-000000000001
|
||||||
|
# [ UUID >-----------------------------------
|
||||||
|
|
||||||
info:
|
info:
|
||||||
# Configure your name!
|
# Configure your name!
|
||||||
name: Joker
|
name: Joker
|
||||||
year: 24
|
how-old-are-you: 24
|
||||||
```
|
```
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
@@ -159,10 +182,10 @@ info:
|
|||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments. -->
|
<!-- JSON file-based implementation, compatible with all Java environments. -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<artifactId>easyconfiguration-json</artifactId>
|
<artifactId>easyconfiguration-gson</artifactId>
|
||||||
<version>[LATEST RELEASE]</version>
|
<version>[LATEST RELEASE]</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -205,8 +228,8 @@ dependencies {
|
|||||||
// YAML file-based implementation, compatible with all Java environments.
|
// YAML file-based implementation, compatible with all Java environments.
|
||||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
||||||
|
|
||||||
// JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments.
|
// JSON file-based implementation, compatible with all Java environments.
|
||||||
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
|
api "cc.carm.lib:easyconfiguration-gson:[LATEST RELEASE]"
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -220,7 +243,8 @@ dependencies {
|
|||||||
EasyConfiguration for MineCraft!
|
EasyConfiguration for MineCraft!
|
||||||
Easily manage configurations on MineCraft-related server platforms.
|
Easily manage configurations on MineCraft-related server platforms.
|
||||||
|
|
||||||
Currently supports BungeeCord, Bukkit (Spigot) servers, with more platforms to be supported soon.
|
Currently, it supports BungeeCord, Velocity, Bukkit (Spigot) servers,
|
||||||
|
with more platforms to be supported soon.
|
||||||
|
|
||||||
## Support and Donation
|
## Support and Donation
|
||||||
|
|
||||||
@@ -232,6 +256,11 @@ Many thanks to Jetbrains for kindly providing a license for us to work on this a
|
|||||||
|
|
||||||
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/EasyConfiguration)
|
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/EasyConfiguration)
|
||||||
|
|
||||||
|
Many thanks to [ArtformGames](https://github.com/ArtformGames) for their
|
||||||
|
strong support and active contribution to this project!
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/ArtformGames/.github/master/logo/logo_full.svg" width="317px" height="117px" alt="ArtformGames">
|
||||||
|
|
||||||
## Open Source License
|
## Open Source License
|
||||||
|
|
||||||
This project's source code is licensed under
|
This project's source code is licensed under
|
||||||
|
|||||||
+44
-17
@@ -32,26 +32,35 @@ README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
|||||||
|
|
||||||
## 开发
|
## 开发
|
||||||
|
|
||||||
详细开发介绍请 [点击这里](.doc/README.md) , JavaDoc(最新Release) 请 [点击这里](https://CarmJos.github.io/EasyConfiguration) 。
|
详细开发介绍请 [点击这里](.doc/README.md) , JavaDoc(最新Release)
|
||||||
|
请 [点击这里](https://CarmJos.github.io/EasyConfiguration) 。
|
||||||
|
|
||||||
### 示例代码
|
### 示例代码
|
||||||
|
|
||||||
为快速的展示该项目的适用性,这里有几个实际演示:
|
为快速的展示该项目的适用性,这里有几个实际演示:
|
||||||
|
|
||||||
- [数据库配置文件实例](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java)
|
- [数据库配置文件实例](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java)
|
||||||
- [全种类配置实例类演示](demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java)
|
- [全种类配置实例类演示](demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java)
|
||||||
|
|
||||||
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo) 直接查看现有的代码演示,更多复杂情况演示详见 [开发介绍](.doc/README.md) 。
|
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo)
|
||||||
|
直接查看现有的代码演示,更多复杂情况演示详见 [开发介绍](.doc/README.md) 。
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@HeaderComment("Configurations for sample")
|
|
||||||
interface SampleConfig extends Configuration {
|
|
||||||
|
|
||||||
@InlineComment("Enabled?") // 行内注释
|
@ConfigPath(root = true)
|
||||||
|
@HeaderComments("Configurations for sample")
|
||||||
|
public interface SampleConfig extends Configuration {
|
||||||
|
|
||||||
|
@InlineComment("Enabled?") // 行后注释
|
||||||
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
|
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
|
||||||
|
|
||||||
|
@HeaderComments("Server configurations") // 头部注释
|
||||||
|
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class);
|
||||||
|
|
||||||
|
@HeaderComments({"[ UUID >-----------------------------------", "A lot of UUIDs"})
|
||||||
|
@FooterComments("[ UUID >-----------------------------------")
|
||||||
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
|
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
|
||||||
.parseValue(UUID::fromString).serializeValue(UUID::toString)
|
.parse(UUID::fromString).serialize(UUID::toString)
|
||||||
.defaults(
|
.defaults(
|
||||||
UUID.fromString("00000000-0000-0000-0000-000000000000"),
|
UUID.fromString("00000000-0000-0000-0000-000000000000"),
|
||||||
UUID.fromString("00000000-0000-0000-0000-000000000001")
|
UUID.fromString("00000000-0000-0000-0000-000000000001")
|
||||||
@@ -59,26 +68,34 @@ interface SampleConfig extends Configuration {
|
|||||||
|
|
||||||
interface INFO extends Configuration {
|
interface INFO extends Configuration {
|
||||||
|
|
||||||
@HeaderComment("Configure your name!") // 头部注释
|
@HeaderComments("Configure your name!") // Header comment
|
||||||
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
|
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
|
||||||
|
|
||||||
@ConfigPath("year") // 自定义配置路径,若不定义,则按照默认规则生成
|
@ConfigPath("how-old-are-you") // 自定义路径
|
||||||
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
|
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Sample {
|
public class Sample {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// 1. 生成一个 “Provider” 用于给配置类提供源配置的文件。
|
// 1. 生成一个 “holder” 用于给配置类提供源配置的文件。
|
||||||
ConfigurationProvider<?> provider = EasyConfiguration.from("config.yml");
|
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/config.yml")
|
||||||
// 2. 通过 “Provider” 初始化配置类或配置实例。
|
.resourcePath("configs/sample.yml")
|
||||||
provider.initialize(SampleConfig.class);
|
.indent(4) // Optional: Set the indentation of the configuration file.
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 2. 通过 “holder” 初始化配置类或配置实例。
|
||||||
|
holder.initialize(SampleConfig.class);
|
||||||
// 3. 现在可以享受快捷方便的配置文件使用方式了~
|
// 3. 现在可以享受快捷方便的配置文件使用方式了~
|
||||||
|
System.out.println("Enabled? -> " + SampleConfig.ENABLED.resolve());
|
||||||
SampleConfig.ENABLED.set(false);
|
SampleConfig.ENABLED.set(false);
|
||||||
System.out.println("Your name is " + SampleConfig.INFO.NAME.getNotNull() + " !");
|
System.out.println("And now? -> " + SampleConfig.ENABLED.resolve());
|
||||||
|
// p.s. 在本示例里的更改未保存,因此启用值在下次运行中仍将为 true。
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -88,14 +105,20 @@ public class Sample {
|
|||||||
|
|
||||||
enabled: true #Enabled?
|
enabled: true #Enabled?
|
||||||
|
|
||||||
|
# Server configurations
|
||||||
|
port:
|
||||||
|
|
||||||
|
# [ UUID >-----------------------------------
|
||||||
|
# A lot of UUIDs
|
||||||
uuids:
|
uuids:
|
||||||
- 00000000-0000-0000-0000-000000000000
|
- 00000000-0000-0000-0000-000000000000
|
||||||
- 00000000-0000-0000-0000-000000000001
|
- 00000000-0000-0000-0000-000000000001
|
||||||
|
# [ UUID >-----------------------------------
|
||||||
|
|
||||||
info:
|
info:
|
||||||
# Configure your name!
|
# Configure your name!
|
||||||
name: Joker
|
name: Joker
|
||||||
year: 24
|
how-old-are-you: 24
|
||||||
```
|
```
|
||||||
|
|
||||||
### 依赖方式
|
### 依赖方式
|
||||||
@@ -164,7 +187,7 @@ info:
|
|||||||
<!--需要注意的是,JSON不支持文件注释。-->
|
<!--需要注意的是,JSON不支持文件注释。-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<artifactId>easyconfiguration-json</artifactId>
|
<artifactId>easyconfiguration-gson</artifactId>
|
||||||
<version>[LATEST RELEASE]</version>
|
<version>[LATEST RELEASE]</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -225,7 +248,7 @@ dependencies {
|
|||||||
|
|
||||||
//基于JSON文件的实现版本,可用于全部Java环境。
|
//基于JSON文件的实现版本,可用于全部Java环境。
|
||||||
//需要注意的是,JSON不支持文件注释。
|
//需要注意的是,JSON不支持文件注释。
|
||||||
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
|
api "cc.carm.lib:easyconfiguration-gson:[LATEST RELEASE]"
|
||||||
|
|
||||||
api "cc.carm.lib:easyconfiguration-hocon:[LATEST RELEASE]"
|
api "cc.carm.lib:easyconfiguration-hocon:[LATEST RELEASE]"
|
||||||
|
|
||||||
@@ -255,6 +278,10 @@ EasyConfiguration for MineCraft!
|
|||||||
|
|
||||||
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/EasyConfiguration)
|
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/EasyConfiguration)
|
||||||
|
|
||||||
|
万分感谢来自 [ArtformGames](https://github.com/ArtformGames) 对本项目的大力支持与积极贡献!
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/ArtformGames/.github/master/logo/logo_full.svg" width="317px" height="117px" alt="ArtformGames">
|
||||||
|
|
||||||
## 开源协议
|
## 开源协议
|
||||||
|
|
||||||
本项目源码采用 [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html) 开源协议。
|
本项目源码采用 [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html) 开源协议。
|
||||||
|
|||||||
+1
-1
@@ -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.0</version>
|
<version>4.0.11</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
|
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -157,7 +158,7 @@ public abstract class ValueType<T> {
|
|||||||
ParameterizedType pt = (ParameterizedType) type;
|
ParameterizedType pt = (ParameterizedType) type;
|
||||||
Type raw = pt.getRawType();
|
Type raw = pt.getRawType();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(raw.getTypeName());
|
sb.append(raw.getClass().getName());
|
||||||
sb.append('<');
|
sb.append('<');
|
||||||
Type[] args = pt.getActualTypeArguments();
|
Type[] args = pt.getActualTypeArguments();
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
@@ -166,6 +167,7 @@ public abstract class ValueType<T> {
|
|||||||
}
|
}
|
||||||
sb.append(args[i].getTypeName());
|
sb.append(args[i].getTypeName());
|
||||||
}
|
}
|
||||||
|
sb.append('>');
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
return type.getTypeName();
|
return type.getTypeName();
|
||||||
|
|||||||
+12
-10
@@ -3,8 +3,8 @@ package cc.carm.lib.configuration.builder.impl;
|
|||||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
import cc.carm.lib.configuration.adapter.ValueType;
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
|
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
|
||||||
import cc.carm.lib.configuration.function.DataConsumer;
|
|
||||||
import cc.carm.lib.configuration.function.DataFunction;
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.function.ValueConsumer;
|
||||||
import cc.carm.lib.configuration.function.ValueHandler;
|
import cc.carm.lib.configuration.function.ValueHandler;
|
||||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
import cc.carm.lib.configuration.value.ConfigValue;
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
@@ -34,28 +34,30 @@ public abstract class AbstractSectionBuilder<
|
|||||||
this.serializer = serializer;
|
this.serializer = serializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull SELF parse(DataFunction<ConfigureSection, PARAM> valueParser) {
|
public @NotNull SELF parse(@NotNull DataFunction<ConfigureSection, PARAM> valueParser) {
|
||||||
return parse((p, section) -> valueParser.handle(section));
|
return parse((p, section) -> valueParser.handle(section));
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull SELF parse(ValueHandler<ConfigureSection, PARAM> valueParser) {
|
public @NotNull SELF parse(@NotNull ValueHandler<ConfigureSection, PARAM> valueParser) {
|
||||||
this.parser = valueParser;
|
this.parser = valueParser;
|
||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull SELF serialize(DataFunction<PARAM, ? extends Map<String, Object>> serializer) {
|
public @NotNull SELF serialize(@NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
|
||||||
return serialize((p, value) -> serializer.handle(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SELF serialize(ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
|
|
||||||
this.serializer = serializer;
|
this.serializer = serializer;
|
||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull SELF serialize(DataConsumer<Map<String, Object>> serializer) {
|
public @NotNull SELF serialize(@NotNull DataFunction<PARAM, ? extends Map<String, Object>> serializer) {
|
||||||
return serialize((p, value) -> {
|
return serialize((p, value) -> {
|
||||||
|
return serializer.handle(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(@NotNull ValueConsumer<Map<String, Object>, PARAM> serializer) {
|
||||||
|
return serialize((h, value) -> {
|
||||||
Map<String, Object> map = new LinkedHashMap<>();
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
serializer.accept(map);
|
serializer.accept(h, map, value);
|
||||||
return map;
|
return map;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ public abstract class AbstractSourceBuilder<
|
|||||||
this.valueSerializer = serializer;
|
this.valueSerializer = serializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull SELF parse(DataFunction<SOURCE, PARAM> parser) {
|
public @NotNull SELF parse(@NotNull DataFunction<SOURCE, PARAM> parser) {
|
||||||
return parse((p, source) -> parser.handle(source));
|
return parse((p, source) -> parser.handle(source));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package cc.carm.lib.configuration.function;
|
||||||
|
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueConsumer<U, T> {
|
||||||
|
|
||||||
|
void accept(@NotNull ConfigurationHolder<?> holder, @NotNull U unit, @NotNull T data) throws Exception;
|
||||||
|
|
||||||
|
default ValueConsumer<U, T> andThen(ValueConsumer<? super U, ? super T> after) {
|
||||||
|
return (holder, unit, data) -> {
|
||||||
|
accept(holder, unit, data);
|
||||||
|
after.accept(holder, unit, data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -20,6 +20,13 @@ import java.util.function.Consumer;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConfigurationFactory, used to create configuration holder.
|
||||||
|
*
|
||||||
|
* @param <SOURCE> The {@link ConfigureSource} type
|
||||||
|
* @param <HOLDER> The {@link ConfigurationHolder} type.
|
||||||
|
* @param <SELF> Self builder, for further implement support.
|
||||||
|
*/
|
||||||
public abstract class ConfigurationFactory<
|
public abstract class ConfigurationFactory<
|
||||||
SOURCE extends ConfigureSource<?, ?, SOURCE>,
|
SOURCE extends ConfigureSource<?, ?, SOURCE>,
|
||||||
HOLDER extends ConfigurationHolder<SOURCE>,
|
HOLDER extends ConfigurationHolder<SOURCE>,
|
||||||
@@ -139,18 +146,39 @@ public abstract class ConfigurationFactory<
|
|||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supply the base path generator for this configuration holder
|
||||||
|
*
|
||||||
|
* @param generator {@link PathGenerator}
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
public SELF pathGenerator(PathGenerator generator) {
|
public SELF pathGenerator(PathGenerator generator) {
|
||||||
return initializer(loader -> {
|
return initializer(loader -> {
|
||||||
loader.pathGenerator(generator);
|
loader.pathGenerator(generator);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new annotation for metadata to the configuration loader
|
||||||
|
*
|
||||||
|
* @param annotation The {@link Annotation}
|
||||||
|
* @param metadata The {@link ConfigurationMetadata} type
|
||||||
|
* @param extractor The {@link Function} to extract the metadata from the annotation
|
||||||
|
* @param <M> The metadata type
|
||||||
|
* @param <A> The annotation type
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
public <M, A extends Annotation> SELF metaAnnotation(@NotNull Class<A> annotation,
|
public <M, A extends Annotation> SELF metaAnnotation(@NotNull Class<A> annotation,
|
||||||
@NotNull ConfigurationMetadata<M> metadata,
|
@NotNull ConfigurationMetadata<M> metadata,
|
||||||
@NotNull Function<A, M> extractor) {
|
@NotNull Function<A, M> extractor) {
|
||||||
return initializer(loader -> loader.registerAnnotation(annotation, metadata, extractor));
|
return initializer(loader -> loader.registerAnnotation(annotation, metadata, extractor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the configuration holder.
|
||||||
|
*
|
||||||
|
* @return The configuration holder
|
||||||
|
*/
|
||||||
public abstract @NotNull HOLDER build();
|
public abstract @NotNull HOLDER build();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ import cc.carm.lib.configuration.adapter.ValueType;
|
|||||||
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||||
|
import cc.carm.lib.configuration.source.meta.StandardMeta;
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||||
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
||||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
import cc.carm.lib.configuration.value.ValueManifest;
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -17,6 +20,8 @@ import org.jetbrains.annotations.UnmodifiableView;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, SOURCE>> {
|
public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, SOURCE>> {
|
||||||
|
|
||||||
@@ -50,6 +55,10 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <O> @NotNull O option(@NotNull ConfigurationOption<O> option) {
|
||||||
|
return options().get(option);
|
||||||
|
}
|
||||||
|
|
||||||
public @NotNull Map<String, ConfigurationMetaHolder> metadata() {
|
public @NotNull Map<String, ConfigurationMetaHolder> metadata() {
|
||||||
return this.metadata;
|
return this.metadata;
|
||||||
}
|
}
|
||||||
@@ -61,14 +70,26 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
|
|||||||
@NotNull
|
@NotNull
|
||||||
@UnmodifiableView
|
@UnmodifiableView
|
||||||
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type) {
|
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type) {
|
||||||
|
return extractMetadata(type, Objects::nonNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type, @NotNull Predicate<M> filter) {
|
||||||
Map<String, M> metas = new LinkedHashMap<>();
|
Map<String, M> metas = new LinkedHashMap<>();
|
||||||
for (Map.Entry<String, ConfigurationMetaHolder> entry : this.metadata.entrySet()) {
|
for (Map.Entry<String, ConfigurationMetaHolder> entry : this.metadata.entrySet()) {
|
||||||
M data = entry.getValue().get(type);
|
M data = entry.getValue().get(type);
|
||||||
if (data != null) metas.put(entry.getKey(), data);
|
if (filter.test(data)) metas.put(entry.getKey(), data);
|
||||||
}
|
}
|
||||||
return Collections.unmodifiableMap(metas);
|
return Collections.unmodifiableMap(metas);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
public Map<String, ConfigValue<?>> registeredValues() {
|
||||||
|
return extractMetadata(StandardMeta.VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
public ValueAdapterRegistry adapters() {
|
public ValueAdapterRegistry adapters() {
|
||||||
return this.adapters;
|
return this.adapters;
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-12
@@ -5,26 +5,27 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ConfigInitializeHandler<T> {
|
public interface ConfigInitializeHandler<T, V> {
|
||||||
|
|
||||||
static <T> ConfigInitializeHandler<T> start() {
|
static <T, V> ConfigInitializeHandler<T, V> start() {
|
||||||
return (provider, path, value) -> {
|
return (provider, path, value, instace) -> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void whenInitialize(@NotNull ConfigurationHolder<?> holder, @Nullable String path, @NotNull T value) throws Exception;
|
void whenInitialize(@NotNull ConfigurationHolder<?> holder, @Nullable String path,
|
||||||
|
@NotNull T value, @Nullable V instance) throws Exception;
|
||||||
|
|
||||||
default ConfigInitializeHandler<T> andThen(ConfigInitializeHandler<T> after) {
|
default ConfigInitializeHandler<T, V> andThen(ConfigInitializeHandler<T, V> after) {
|
||||||
return (provider, path, value) -> {
|
return (provider, path, value, instance) -> {
|
||||||
whenInitialize(provider, path, value);
|
whenInitialize(provider, path, value, instance);
|
||||||
after.whenInitialize(provider, path, value);
|
after.whenInitialize(provider, path, value, instance);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
default ConfigInitializeHandler<T> compose(ConfigInitializeHandler<T> before) {
|
default ConfigInitializeHandler<T, V> compose(ConfigInitializeHandler<T, V> before) {
|
||||||
return (provider, path, value) -> {
|
return (provider, path, value, instance) -> {
|
||||||
before.whenInitialize(provider, path, value);
|
before.whenInitialize(provider, path, value, instance);
|
||||||
whenInitialize(provider, path, value);
|
whenInitialize(provider, path, value, instance);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+29
-24
@@ -3,6 +3,7 @@ package cc.carm.lib.configuration.source.loader;
|
|||||||
import cc.carm.lib.configuration.Configuration;
|
import cc.carm.lib.configuration.Configuration;
|
||||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||||
|
import cc.carm.lib.configuration.source.meta.StandardMeta;
|
||||||
import cc.carm.lib.configuration.source.option.StandardOptions;
|
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||||
import cc.carm.lib.configuration.value.ConfigValue;
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -20,18 +21,18 @@ import java.util.function.Function;
|
|||||||
public class ConfigurationInitializer {
|
public class ConfigurationInitializer {
|
||||||
|
|
||||||
protected @NotNull PathGenerator pathGenerator;
|
protected @NotNull PathGenerator pathGenerator;
|
||||||
protected @NotNull ConfigInitializeHandler<Field> fieldInitializer;
|
protected @NotNull ConfigInitializeHandler<Field, ConfigValue<?>> valueInitializer;
|
||||||
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer;
|
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer;
|
||||||
|
|
||||||
public ConfigurationInitializer() {
|
public ConfigurationInitializer() {
|
||||||
this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start());
|
this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigurationInitializer(@NotNull PathGenerator pathGenerator,
|
public ConfigurationInitializer(@NotNull PathGenerator pathGenerator,
|
||||||
@NotNull ConfigInitializeHandler<Field> fieldInitializer,
|
@NotNull ConfigInitializeHandler<Field, ConfigValue<?>> valueInitializer,
|
||||||
@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
|
@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
|
||||||
this.pathGenerator = pathGenerator;
|
this.pathGenerator = pathGenerator;
|
||||||
this.fieldInitializer = fieldInitializer;
|
this.valueInitializer = valueInitializer;
|
||||||
this.classInitializer = classInitializer;
|
this.classInitializer = classInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,34 +44,34 @@ public class ConfigurationInitializer {
|
|||||||
return pathGenerator;
|
return pathGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigInitializeHandler<Field> fieldInitializer() {
|
public ConfigInitializeHandler<Field, ConfigValue<?>> fieldInitializer() {
|
||||||
return fieldInitializer;
|
return valueInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
|
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?>> fieldInitializer) {
|
||||||
this.fieldInitializer = fieldInitializer;
|
this.valueInitializer = fieldInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConfigInitializeHandler<Class<? extends Configuration>> classInitializer() {
|
public ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer() {
|
||||||
return classInitializer;
|
return classInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void classInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
|
public void classInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
|
||||||
this.classInitializer = classInitializer;
|
this.classInitializer = classInitializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
|
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?>> fieldInitializer) {
|
||||||
this.fieldInitializer = this.fieldInitializer.andThen(fieldInitializer);
|
this.valueInitializer = this.valueInitializer.andThen(fieldInitializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void appendClassInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
|
public void appendClassInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
|
||||||
this.classInitializer = this.classInitializer.andThen(classInitializer);
|
this.classInitializer = this.classInitializer.andThen(classInitializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T, A extends Annotation> void registerClassAnnotation(@NotNull Class<A> annotation,
|
public <T, A extends Annotation> void registerClassAnnotation(@NotNull Class<A> annotation,
|
||||||
@NotNull ConfigurationMetadata<T> metadata,
|
@NotNull ConfigurationMetadata<T> metadata,
|
||||||
@NotNull Function<A, T> extractor) {
|
@NotNull Function<A, T> extractor) {
|
||||||
appendClassInitializer((holder, path, clazz) -> {
|
appendClassInitializer((holder, path, clazz, instance) -> {
|
||||||
A data = clazz.getAnnotation(annotation);
|
A data = clazz.getAnnotation(annotation);
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||||
@@ -80,7 +81,7 @@ public class ConfigurationInitializer {
|
|||||||
public <T, A extends Annotation> void registerFieldAnnotation(@NotNull Class<A> annotation,
|
public <T, A extends Annotation> void registerFieldAnnotation(@NotNull Class<A> annotation,
|
||||||
@NotNull ConfigurationMetadata<T> metadata,
|
@NotNull ConfigurationMetadata<T> metadata,
|
||||||
@NotNull Function<A, T> extractor) {
|
@NotNull Function<A, T> extractor) {
|
||||||
appendFieldInitializer((holder, path, field) -> {
|
appendFieldInitializer((holder, path, field, instance) -> {
|
||||||
A data = field.getAnnotation(annotation);
|
A data = field.getAnnotation(annotation);
|
||||||
if (data == null) return;
|
if (data == null) return;
|
||||||
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||||
@@ -107,13 +108,13 @@ public class ConfigurationInitializer {
|
|||||||
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
||||||
@NotNull Configuration config) throws Exception {
|
@NotNull Configuration config) throws Exception {
|
||||||
initializeInstance(holder, config, null, null);
|
initializeInstance(holder, config, null, null);
|
||||||
if (holder.options().get(StandardOptions.SET_DEFAULTS)) holder.save();
|
if (holder.option(StandardOptions.SET_DEFAULTS)) holder.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
||||||
@NotNull Class<? extends Configuration> clazz) throws Exception {
|
@NotNull Class<? extends Configuration> clazz) throws Exception {
|
||||||
initializeStaticClass(holder, clazz, null, null);
|
initializeStaticClass(holder, clazz, null, null);
|
||||||
if (holder.options().get(StandardOptions.SET_DEFAULTS)) holder.save();
|
if (holder.option(StandardOptions.SET_DEFAULTS)) holder.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -122,7 +123,7 @@ public class ConfigurationInitializer {
|
|||||||
@Nullable String parentPath, @Nullable Field configField) {
|
@Nullable String parentPath, @Nullable Field configField) {
|
||||||
String path = getClassPath(holder, parentPath, root.getClass(), configField);
|
String path = getClassPath(holder, parentPath, root.getClass(), configField);
|
||||||
try {
|
try {
|
||||||
this.classInitializer.whenInitialize(holder, path, root.getClass());
|
this.classInitializer.whenInitialize(holder, path, root.getClass(), root);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -139,7 +140,7 @@ public class ConfigurationInitializer {
|
|||||||
String path = getClassPath(holder, parentPath, clazz, configField);
|
String path = getClassPath(holder, parentPath, clazz, configField);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz);
|
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz, configField);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -148,7 +149,7 @@ public class ConfigurationInitializer {
|
|||||||
initializeField(holder, clazz, field, path);
|
initializeField(holder, clazz, field, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (holder.options().get(StandardOptions.LOAD_SUB_CLASSES)) {
|
if (holder.option(StandardOptions.LOAD_SUB_CLASSES)) {
|
||||||
Class<?>[] classes = clazz.getDeclaredClasses();
|
Class<?>[] classes = clazz.getDeclaredClasses();
|
||||||
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
|
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
|
||||||
initializeStaticClass(holder, classes[i], path, null);
|
initializeStaticClass(holder, classes[i], path, null);
|
||||||
@@ -168,13 +169,17 @@ public class ConfigurationInitializer {
|
|||||||
String path = getFieldPath(holder, parent, field);
|
String path = getFieldPath(holder, parent, field);
|
||||||
if (path == null) return;
|
if (path == null) return;
|
||||||
value.initialize(holder, path);
|
value.initialize(holder, path);
|
||||||
|
holder.metadata(path).set(StandardMeta.VALUE, value); // Mark the minimal config value unit.
|
||||||
|
if (holder.option(StandardOptions.SET_DEFAULTS)) {
|
||||||
|
value.setDefault(); // Set default value.
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
this.fieldInitializer.whenInitialize(holder, path, field);
|
this.valueInitializer.whenInitialize(holder, path, field, value);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
if (holder.options().get(StandardOptions.SET_DEFAULTS)) {
|
if (holder.option(StandardOptions.PRELOAD)) {
|
||||||
value.setDefault();
|
value.get(); // Preload the value by calling #get method.
|
||||||
}
|
}
|
||||||
} else if (source instanceof Configuration && object instanceof Configuration) {
|
} else if (source instanceof Configuration && object instanceof Configuration) {
|
||||||
// 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。
|
// 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.function.UnaryOperator;
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path generator for configuration.
|
||||||
|
* <p>
|
||||||
|
* Path generator is a utility class that helps to generate the path of the configuration.
|
||||||
|
* It can be used to generate the path of the field or class.
|
||||||
|
*/
|
||||||
public class PathGenerator {
|
public class PathGenerator {
|
||||||
|
|
||||||
public static PathGenerator of() {
|
public static PathGenerator of() {
|
||||||
@@ -80,7 +86,7 @@ public class PathGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static char pathSeparator(ConfigurationHolder<?> holder) {
|
public static char pathSeparator(ConfigurationHolder<?> holder) {
|
||||||
return holder.options().get(StandardOptions.PATH_SEPARATOR);
|
return holder.option(StandardOptions.PATH_SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package cc.carm.lib.configuration.source.meta;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
|
||||||
|
public interface StandardMeta {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To mark the {@link ConfigValue} instance of specific path.
|
||||||
|
*/
|
||||||
|
ConfigurationMetadata<ConfigValue<?>> VALUE = ConfigurationMetadata.of();
|
||||||
|
|
||||||
|
}
|
||||||
+2
-14
@@ -6,32 +6,20 @@ import java.util.function.Supplier;
|
|||||||
|
|
||||||
public class ConfigurationOption<V> {
|
public class ConfigurationOption<V> {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T> ConfigurationOption<T> of(@NotNull T defaultValue) {
|
public static <T> ConfigurationOption<T> of(@NotNull T defaultValue) {
|
||||||
return of((Class<T>) defaultValue.getClass(), defaultValue);
|
return new ConfigurationOption<>(defaultValue);
|
||||||
}
|
|
||||||
|
|
||||||
public static <T> ConfigurationOption<T> of(@NotNull Class<T> valueClazz, @NotNull T defaultValue) {
|
|
||||||
return new ConfigurationOption<>(valueClazz, defaultValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> ConfigurationOption<T> of(@NotNull Supplier<T> defaultValue) {
|
public static <T> ConfigurationOption<T> of(@NotNull Supplier<T> defaultValue) {
|
||||||
return of(defaultValue.get());
|
return of(defaultValue.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private final @NotNull Class<V> valueClazz;
|
|
||||||
private @NotNull V defaultValue;
|
private @NotNull V defaultValue;
|
||||||
|
|
||||||
public ConfigurationOption(@NotNull Class<V> valueClazz, @NotNull V defaultValue) {
|
public ConfigurationOption(@NotNull V defaultValue) {
|
||||||
this.valueClazz = valueClazz;
|
|
||||||
this.defaultValue = defaultValue;
|
this.defaultValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Class<V> valueClass() {
|
|
||||||
return this.valueClazz;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull V defaults() {
|
public @NotNull V defaults() {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,12 +23,13 @@ public interface StandardOptions {
|
|||||||
ConfigurationOption<Boolean> LOAD_SUB_CLASSES = of(true);
|
ConfigurationOption<Boolean> LOAD_SUB_CLASSES = of(true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to pre parse the config values.
|
* Whether to pre parse the config values,
|
||||||
* <br> if false, the values will be parsed when calling
|
* may good to set true if you want to keep the config format.
|
||||||
* {@link cc.carm.lib.configuration.value.ConfigValue#get()}
|
|
||||||
* <br> if true, the values will be parsed when
|
* <br> if true, the values will be parsed when
|
||||||
* {@link ConfigurationHolder#initialize(Configuration)}.
|
* {@link ConfigurationHolder#initialize(Configuration)}.
|
||||||
|
* <br> if false, the values will be parsed when calling
|
||||||
|
* {@link cc.carm.lib.configuration.value.ConfigValue#get()}
|
||||||
*/
|
*/
|
||||||
ConfigurationOption<Boolean> PRELOAD = of(false);
|
ConfigurationOption<Boolean> PRELOAD = of(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+596
-23
@@ -1,73 +1,451 @@
|
|||||||
package cc.carm.lib.configuration.source.section;
|
package cc.carm.lib.configuration.source.section;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.function.DataFunction;
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
import cc.carm.lib.configuration.source.option.StandardOptions;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.annotations.UnmodifiableView;
|
import org.jetbrains.annotations.UnmodifiableView;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a section of a configuration.
|
||||||
|
*
|
||||||
|
* @author Carm
|
||||||
|
* @since 4.0.0
|
||||||
|
*/
|
||||||
public interface ConfigureSection {
|
public interface ConfigureSection {
|
||||||
|
|
||||||
@NotNull ConfigureSource<?, ?, ?> source();
|
/**
|
||||||
|
* Gets the parent section of this section.
|
||||||
|
* <p>
|
||||||
|
* For root sections, this will return null.
|
||||||
|
*
|
||||||
|
* @return Parent section, or null if this is a root section.
|
||||||
|
*/
|
||||||
|
@Contract(pure = true)
|
||||||
@Nullable ConfigureSection parent();
|
@Nullable ConfigureSection parent();
|
||||||
|
|
||||||
default char separator() {
|
/**
|
||||||
return source().holder().options().get(StandardOptions.PATH_SEPARATOR);
|
* Get the current section's path from {@link #parent()} of this section.
|
||||||
|
*
|
||||||
|
* @return The current path of this section, if {@link #isRoot()}, return empty string.
|
||||||
|
*/
|
||||||
|
@NotNull String path();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full path of this section.
|
||||||
|
*
|
||||||
|
* @return The full path of this section, if {@link #isRoot()}, return empty string.
|
||||||
|
*/
|
||||||
|
default @NotNull String fullPath() {
|
||||||
|
if (parent() == null) return "";
|
||||||
|
return (parent().isRoot() ? "" : parent().fullPath() + pathSeparator()) + path();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the path separator for the section.
|
||||||
|
*
|
||||||
|
* @return The path separator
|
||||||
|
*/
|
||||||
|
default char pathSeparator() {
|
||||||
|
return '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if this section is a root section.
|
||||||
|
*
|
||||||
|
* @return True if this section is a root section, false otherwise.
|
||||||
|
*/
|
||||||
|
@Contract(pure = true)
|
||||||
|
default boolean isRoot() {
|
||||||
|
return parent() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if this section is empty.
|
||||||
|
*
|
||||||
|
* @return True if this section is empty, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean isEmpty() {
|
||||||
|
return getValues(false).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of keys in this section.
|
||||||
|
*
|
||||||
|
* @return Number of keys in this section.
|
||||||
|
*/
|
||||||
|
default int size(boolean deep) {
|
||||||
|
return getKeys(deep).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set containing all keys in this section.
|
||||||
|
* <p>
|
||||||
|
* If deep is set to true, then this will contain all the keys within any
|
||||||
|
* child {@link ConfigureSection}s (and their children paths).
|
||||||
|
* <p>
|
||||||
|
* If deep is set to false, then this will contain only the keys of any
|
||||||
|
* direct children, and not their own children.
|
||||||
|
*
|
||||||
|
* @param deep Whether to get a deep list.
|
||||||
|
* @return Set of keys contained within this Section.
|
||||||
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
@UnmodifiableView
|
@UnmodifiableView
|
||||||
default Set<String> getKeys(boolean deep) {
|
default Set<String> getKeys(boolean deep) {
|
||||||
return getValues(deep).keySet();
|
return getValues(deep).keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set containing all primary keys in this section.
|
||||||
|
*
|
||||||
|
* @return Set of keys contained within this Section.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
default Set<String> keys() {
|
||||||
|
return getKeys(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set containing all values in this section.
|
||||||
|
* <p>
|
||||||
|
* If deep is set to true, then this will contain all the keys within any
|
||||||
|
* child {@link ConfigureSection}s (and their children paths).
|
||||||
|
* <p>
|
||||||
|
* If deep is set to false, then this will contain only the keys of any
|
||||||
|
* direct children, and not their own children.
|
||||||
|
*
|
||||||
|
* @param deep Whether to get a deep list.
|
||||||
|
* @return Map of data values contained within this Section.
|
||||||
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
@UnmodifiableView
|
@UnmodifiableView
|
||||||
Map<String, Object> getValues(boolean deep);
|
Map<String, Object> getValues(boolean deep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set containing all key-values in this section.
|
||||||
|
*
|
||||||
|
* @return Map of data values contained within this Section.
|
||||||
|
* @see #getValues(boolean)
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
default Map<String, Object> values() {
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @return Stream of all values in this section.
|
||||||
|
*/
|
||||||
|
default Stream<Map.Entry<String, Object>> stream() {
|
||||||
|
return values().entrySet().stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over all key-values in this section (include child sections)
|
||||||
|
*
|
||||||
|
* @param action The action to apply to each key.
|
||||||
|
*/
|
||||||
|
default void forEach(@NotNull BiConsumer<String, Object> action) {
|
||||||
|
values().forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value at the given path.
|
||||||
|
* <p>
|
||||||
|
* Null values will be kept, if you want to remove a value use {@link #remove(String)}
|
||||||
|
* Path separator depends on holder's
|
||||||
|
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||||
|
*
|
||||||
|
* @param path The path to set the value at.
|
||||||
|
* @param value The value to set.
|
||||||
|
*/
|
||||||
void set(@NotNull String path, @Nullable Object value);
|
void set(@NotNull String path, @Nullable Object value);
|
||||||
|
|
||||||
boolean contains(@NotNull String path);
|
/**
|
||||||
|
* Removes the value at the given path.
|
||||||
|
* <p>
|
||||||
|
* Path separator depends on holder's
|
||||||
|
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||||
|
*
|
||||||
|
* @param path The path to remove the value at.
|
||||||
|
*/
|
||||||
|
void remove(@NotNull String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given path is present.
|
||||||
|
* <p>
|
||||||
|
* Path separator depends on holder's
|
||||||
|
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean contains(@NotNull String path) {
|
||||||
|
return getKeys(true).contains(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the value of given path is present.
|
||||||
|
* <p>
|
||||||
|
* Path separator depends on holder's
|
||||||
|
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean containsValue(@NotNull String path) {
|
||||||
|
return get(path) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is specific type.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @param typeClass The type's class
|
||||||
|
* @param <T> The type to check.
|
||||||
|
* @return True if the value is present and is the correct type, false otherwise.
|
||||||
|
*/
|
||||||
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
|
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
|
||||||
return typeClass.isInstance(get(path));
|
return typeClass.isInstance(get(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link List}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a list, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isList(@NotNull String path) {
|
default boolean isList(@NotNull String path) {
|
||||||
return isType(path, List.class);
|
return isType(path, List.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable List<?> getList(@NotNull String path);
|
/**
|
||||||
|
* Get the value as a {@link List} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the {@link List}.
|
||||||
|
* @return The list if the path exists and is a list, otherwise null.
|
||||||
|
*/
|
||||||
|
default @Nullable List<?> getList(@NotNull String path) {
|
||||||
|
Object val = get(path);
|
||||||
|
return (val instanceof List<?>) ? (List<?>) val : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a section, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isSection(@NotNull String path) {
|
default boolean isSection(@NotNull String path) {
|
||||||
return isType(path, ConfigureSection.class);
|
return isType(path, ConfigureSection.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link ConfigureSection} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the section.
|
||||||
|
* @return The section if the path exists and is a section, otherwise null.
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
ConfigureSection getSection(@NotNull String path);
|
default ConfigureSection getSection(@NotNull String path) {
|
||||||
|
Object val = get(path);
|
||||||
@Nullable Object get(@NotNull String path);
|
return (val instanceof ConfigureSection) ? (ConfigureSection) val : null;
|
||||||
|
|
||||||
default @Nullable <T> T get(@NotNull String path, @NotNull Class<T> clazz) {
|
|
||||||
return get(path, null, clazz);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default @Nullable <T> T get(@NotNull String path, @NotNull DataFunction<Object, T> parser) {
|
/**
|
||||||
|
* Creates a new {@link ConfigureSection} with specified values.
|
||||||
|
* <p> The {@link #parent()} of the new section will be this section.
|
||||||
|
* <p> This section will not be saved until {@link #set(String, Object)} is called.
|
||||||
|
* <p> If you want to create and use a section and set it to this section, use {@link #computeSection(String)}.
|
||||||
|
*
|
||||||
|
* @param data The data to be used to create section.
|
||||||
|
* @return Newly created section
|
||||||
|
*/
|
||||||
|
@NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ConfigureSection} with specified values.
|
||||||
|
* <p> The {@link #parent()} of the new section will be this section.
|
||||||
|
*
|
||||||
|
* @param data The data to be used to create section.
|
||||||
|
* @return Newly created section
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
|
||||||
|
return createSection(path, () -> {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
data.accept(map);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ConfigureSection} with specified values.
|
||||||
|
* <p> The {@link #parent()} of the new section will be this section.
|
||||||
|
*
|
||||||
|
* @param data The data to be used to create section.
|
||||||
|
* @return Newly created section
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
|
||||||
|
return createSection(path, data.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new empty {@link ConfigureSection}.
|
||||||
|
* <p> The {@link #parent()} of the new section will be this section.
|
||||||
|
*
|
||||||
|
* @return Newly created section
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection createSection(@NotNull String path) {
|
||||||
|
return createSection(path, new LinkedHashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a section at the given path.
|
||||||
|
* <p>
|
||||||
|
* Any value previously set at this path will be replaced if it is not a section
|
||||||
|
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to get the section from.
|
||||||
|
* @return The section at the path, or a new section if it does not exist.
|
||||||
|
* @see #createSection(String, Map)
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection computeSection(@NotNull String path) {
|
||||||
|
return computeSection(path, new LinkedHashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a section at the given path.
|
||||||
|
* <p>
|
||||||
|
* Any value previously set at this path will be replaced if it is not a section
|
||||||
|
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to get the section from.
|
||||||
|
* @return The section at the path, or a new section if it does not exist.
|
||||||
|
* @see #createSection(String, Map)
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||||
|
ConfigureSection current = getSection(path);
|
||||||
|
if (current == null) {
|
||||||
|
current = createSection(path, data);
|
||||||
|
set(path, current);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a section at the given path.
|
||||||
|
* <p>
|
||||||
|
* Any value previously set at this path will be replaced if it is not a section
|
||||||
|
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to get the section from.
|
||||||
|
* @return The section at the path, or a new section if it does not exist.
|
||||||
|
* @see #createSection(String, Map)
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
|
||||||
|
return computeSection(path, () -> {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
data.accept(map);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a section at the given path.
|
||||||
|
* <p>
|
||||||
|
* Any value previously set at this path will be replaced if it is not a section
|
||||||
|
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to get the section from.
|
||||||
|
* @return The section at the path, or a new section if it does not exist.
|
||||||
|
* @see #createSection(String, Map)
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
|
||||||
|
return computeSection(path, data.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the origin value of the path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the value from.
|
||||||
|
* @return The value at the path, or null if not found.
|
||||||
|
*/
|
||||||
|
@Nullable Object get(@NotNull String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the path for specific type,
|
||||||
|
* if the path does not exist, return null.
|
||||||
|
*
|
||||||
|
* @param path The path to get the value from.
|
||||||
|
* @param type The type class of the value
|
||||||
|
* @param <T> The type of the value
|
||||||
|
* @return The value at the path, or the default value if not found.
|
||||||
|
*/
|
||||||
|
default @Nullable <T> T get(@NotNull String path, @NotNull Class<T> type) {
|
||||||
|
return get(path, null, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the path using a parser function,
|
||||||
|
* if the path does not exist, return NULL.
|
||||||
|
*
|
||||||
|
* @param path The path to get the value from.
|
||||||
|
* @param parser The function to parse the value
|
||||||
|
* @param <T> The type of the value
|
||||||
|
* @return The value at the path, or null if not found.
|
||||||
|
*/
|
||||||
|
default @Nullable <T> T get(@NotNull String path, @NotNull DataFunction<@Nullable Object, T> parser) {
|
||||||
return get(path, null, parser);
|
return get(path, null, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the path for specific type,
|
||||||
|
* if the path does not exist, return the default value.
|
||||||
|
*
|
||||||
|
* @param path The path to get the value from.
|
||||||
|
* @param defaults The default value to return if the path does not exist.
|
||||||
|
* @param clazz The type class of the value
|
||||||
|
* @param <T> The type of the value
|
||||||
|
* @return The value at the path, or the default value if not found.
|
||||||
|
*/
|
||||||
@Contract("_,!null,_->!null")
|
@Contract("_,!null,_->!null")
|
||||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class<T> clazz) {
|
default @Nullable <T> T get(@NotNull String path, @Nullable T defaults, @NotNull Class<T> clazz) {
|
||||||
return get(path, defaultValue, DataFunction.castObject(clazz));
|
return get(path, defaults, DataFunction.castObject(clazz));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the path using a parser function,
|
||||||
|
* if the path does not exist, return the default value.
|
||||||
|
*
|
||||||
|
* @param path The path to get the value from.
|
||||||
|
* @param defaultValue The default value to return if the path does not exist.
|
||||||
|
* @param parser The function to parse the value
|
||||||
|
* @param <T> The type of the value
|
||||||
|
* @return The value at the path, or the default value if not found.
|
||||||
|
*/
|
||||||
@Contract("_,!null,_->!null")
|
@Contract("_,!null,_->!null")
|
||||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
|
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
|
||||||
@NotNull DataFunction<Object, T> parser) {
|
@NotNull DataFunction<Object, T> parser) {
|
||||||
@@ -82,125 +460,290 @@ public interface ConfigureSection {
|
|||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link Boolean}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a boolean, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isBoolean(@NotNull String path) {
|
default boolean isBoolean(@NotNull String path) {
|
||||||
return isType(path, Boolean.class);
|
return isType(path, Boolean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Boolean} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the boolean.
|
||||||
|
* @return The boolean if the path exists and is a boolean, otherwise false.
|
||||||
|
*/
|
||||||
default boolean getBoolean(@NotNull String path) {
|
default boolean getBoolean(@NotNull String path) {
|
||||||
return getBoolean(path, false);
|
return getBoolean(path, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Boolean} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the boolean.
|
||||||
|
* @param def The default value to return if the path does not exist.
|
||||||
|
* @return The boolean if the path exists and is a boolean, otherwise the default value.
|
||||||
|
*/
|
||||||
@Contract("_, !null -> !null")
|
@Contract("_, !null -> !null")
|
||||||
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
|
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
|
||||||
return get(path, def, DataFunction.booleanValue());
|
return get(path, def, DataFunction.booleanValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link Byte}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a byte, false otherwise.
|
||||||
|
*/
|
||||||
default @Nullable Boolean isByte(@NotNull String path) {
|
default @Nullable Boolean isByte(@NotNull String path) {
|
||||||
return isType(path, Byte.class);
|
return isType(path, Byte.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Byte} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the byte.
|
||||||
|
* @return The byte if the path exists and is a byte, otherwise 0.
|
||||||
|
*/
|
||||||
default @Nullable Byte getByte(@NotNull String path) {
|
default @Nullable Byte getByte(@NotNull String path) {
|
||||||
return getByte(path, (byte) 0);
|
return getByte(path, (byte) 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Byte} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the byte.
|
||||||
|
* @param def The default value to return if the path does not exist.
|
||||||
|
* @return The byte if the path exists and is a byte, otherwise the default value.
|
||||||
|
*/
|
||||||
@Contract("_, !null -> !null")
|
@Contract("_, !null -> !null")
|
||||||
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
|
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
|
||||||
return get(path, def, DataFunction.byteValue());
|
return get(path, def, DataFunction.byteValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link Short}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a short, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isShort(@NotNull String path) {
|
default boolean isShort(@NotNull String path) {
|
||||||
return isType(path, Short.class);
|
return isType(path, Short.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Short} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the short.
|
||||||
|
* @return The short if the path exists and is a short, otherwise 0.
|
||||||
|
*/
|
||||||
default @Nullable Short getShort(@NotNull String path) {
|
default @Nullable Short getShort(@NotNull String path) {
|
||||||
return getShort(path, (short) 0);
|
return getShort(path, (short) 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Short} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the short.
|
||||||
|
* @param def The default value to return if the path does not exist.
|
||||||
|
* @return The short if the path exists and is a short, otherwise the default value.
|
||||||
|
*/
|
||||||
@Contract("_, !null -> !null")
|
@Contract("_, !null -> !null")
|
||||||
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
|
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
|
||||||
return get(path, def, DataFunction.shortValue());
|
return get(path, def, DataFunction.shortValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link Integer}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is an int, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isInt(@NotNull String path) {
|
default boolean isInt(@NotNull String path) {
|
||||||
return isType(path, Integer.class);
|
return isType(path, Integer.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Integer} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the int.
|
||||||
|
* @return The int if the path exists and is an int, otherwise 0.
|
||||||
|
*/
|
||||||
default @Nullable Integer getInt(@NotNull String path) {
|
default @Nullable Integer getInt(@NotNull String path) {
|
||||||
return getInt(path, 0);
|
return getInt(path, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Integer} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the int.
|
||||||
|
* @param def The default value to return if the path does not exist.
|
||||||
|
* @return The int if the path exists and is an int, otherwise the default value.
|
||||||
|
*/
|
||||||
@Contract("_, !null -> !null")
|
@Contract("_, !null -> !null")
|
||||||
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
|
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
|
||||||
return get(path, def, DataFunction.intValue());
|
return get(path, def, DataFunction.intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link Long}.F
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a long, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isLong(@NotNull String path) {
|
default boolean isLong(@NotNull String path) {
|
||||||
return isType(path, Long.class);
|
return isType(path, Long.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Long} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the long.
|
||||||
|
* @return The long if the path exists and is a long, otherwise 0.
|
||||||
|
*/
|
||||||
default @Nullable Long getLong(@NotNull String path) {
|
default @Nullable Long getLong(@NotNull String path) {
|
||||||
return getLong(path, 0L);
|
return getLong(path, 0L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Long} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the long.
|
||||||
|
* @param def The default value to return if the path does not exist.
|
||||||
|
* @return The long if the path exists and is a long, otherwise the default value.
|
||||||
|
*/
|
||||||
@Contract("_, !null -> !null")
|
@Contract("_, !null -> !null")
|
||||||
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
|
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
|
||||||
return get(path, def, DataFunction.longValue());
|
return get(path, def, DataFunction.longValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link Float}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a float, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isFloat(@NotNull String path) {
|
default boolean isFloat(@NotNull String path) {
|
||||||
return isType(path, Float.class);
|
return isType(path, Float.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Float} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the float.
|
||||||
|
* @return The float if the path exists and is a float, otherwise 0.
|
||||||
|
*/
|
||||||
default @Nullable Float getFloat(@NotNull String path) {
|
default @Nullable Float getFloat(@NotNull String path) {
|
||||||
return getFloat(path, 0.0F);
|
return getFloat(path, 0.0F);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Float} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the float.
|
||||||
|
* @param def The default value to return if the path does not exist.
|
||||||
|
* @return The float if the path exists and is a float, otherwise the default value.
|
||||||
|
*/
|
||||||
@Contract("_, !null -> !null")
|
@Contract("_, !null -> !null")
|
||||||
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
|
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
|
||||||
return get(path, def, DataFunction.floatValue());
|
return get(path, def, DataFunction.floatValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link Double}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a double, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isDouble(@NotNull String path) {
|
default boolean isDouble(@NotNull String path) {
|
||||||
return isType(path, Double.class);
|
return isType(path, Double.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Double} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the double.
|
||||||
|
* @return The double if the path exists and is a double, otherwise 0.
|
||||||
|
*/
|
||||||
default @Nullable Double getDouble(@NotNull String path) {
|
default @Nullable Double getDouble(@NotNull String path) {
|
||||||
return getDouble(path, 0.0D);
|
return getDouble(path, 0.0D);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Double} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the double.
|
||||||
|
* @param def The default value to return if the path does not exist.
|
||||||
|
* @return The double if the path exists and is a double, otherwise the default value.
|
||||||
|
*/
|
||||||
@Contract("_, !null -> !null")
|
@Contract("_, !null -> !null")
|
||||||
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
|
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
|
||||||
return get(path, def, DataFunction.doubleValue());
|
return get(path, def, DataFunction.doubleValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link Character}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a char, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isChar(@NotNull String path) {
|
default boolean isChar(@NotNull String path) {
|
||||||
return isType(path, Boolean.class);
|
return isType(path, Boolean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Character} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the char.
|
||||||
|
* @return The char if the path exists and is a char, otherwise null.
|
||||||
|
*/
|
||||||
default @Nullable Character getChar(@NotNull String path) {
|
default @Nullable Character getChar(@NotNull String path) {
|
||||||
return getChar(path, null);
|
return getChar(path, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link Character} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the char.
|
||||||
|
* @param def The default value to return if the path does not exist.
|
||||||
|
* @return The char if the path exists and is a char, otherwise the default value.
|
||||||
|
*/
|
||||||
@Contract("_, !null -> !null")
|
@Contract("_, !null -> !null")
|
||||||
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
|
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
|
||||||
return get(path, def, Character.class);
|
return get(path, def, Character.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link String}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a string, false otherwise.
|
||||||
|
*/
|
||||||
default boolean isString(@NotNull String path) {
|
default boolean isString(@NotNull String path) {
|
||||||
return isType(path, String.class);
|
return isType(path, String.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link String} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the string.
|
||||||
|
* @return The string if the path exists and is a string, otherwise null.
|
||||||
|
*/
|
||||||
default @Nullable String getString(@NotNull String path) {
|
default @Nullable String getString(@NotNull String path) {
|
||||||
return getString(path, null);
|
return getString(path, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value as a {@link String} from the specified path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the string.
|
||||||
|
* @param def The default value to return if the path does not exist.
|
||||||
|
* @return The string if the path exists and is a string, otherwise the default value.
|
||||||
|
*/
|
||||||
@Contract("_, !null -> !null")
|
@Contract("_, !null -> !null")
|
||||||
default @Nullable String getString(@NotNull String path, @Nullable String def) {
|
default @Nullable String getString(@NotNull String path, @Nullable String def) {
|
||||||
return get(path, def, String.class);
|
return get(path, def, String.class);
|
||||||
@@ -298,23 +841,53 @@ public interface ConfigureSection {
|
|||||||
return getList(path, DataFunction.castObject(Character.class));
|
return getList(path, DataFunction.castObject(Character.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the specific type of collection from the section.
|
||||||
|
*
|
||||||
|
* @param path The path to get the collection from
|
||||||
|
* @param constructor The constructor of the collection
|
||||||
|
* @param parser The function to parse the values
|
||||||
|
* @param <T> The type of the values
|
||||||
|
* @param <C> The type of the collection
|
||||||
|
* @return The collection of values
|
||||||
|
*/
|
||||||
default <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path,
|
default <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path,
|
||||||
@NotNull Supplier<C> constructor,
|
@NotNull Supplier<C> constructor,
|
||||||
@NotNull DataFunction<Object, T> parser) {
|
@NotNull DataFunction<Object, T> parser) {
|
||||||
return parseCollection(getList(path), constructor, parser);
|
return parseCollection(getList(path), constructor, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the specific type of steam from the section.
|
||||||
|
*
|
||||||
|
* @param path The path to get the stream from
|
||||||
|
* @return The stream of values
|
||||||
|
*/
|
||||||
default @NotNull Stream<?> stream(@NotNull String path) {
|
default @NotNull Stream<?> stream(@NotNull String path) {
|
||||||
List<?> values = getList(path);
|
List<?> values = getList(path);
|
||||||
return values == null ? Stream.empty() : values.stream();
|
return values == null ? Stream.empty() : values.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the specific type of steam from the section.
|
||||||
|
*
|
||||||
|
* @param path The path to get the stream from
|
||||||
|
* @param parser The function to parse the values
|
||||||
|
* @param <T> The type of the values
|
||||||
|
* @return The stream of values
|
||||||
|
*/
|
||||||
default <T> @NotNull Stream<T> stream(@NotNull String path, @NotNull Function<Object, T> parser) {
|
default <T> @NotNull Stream<T> stream(@NotNull String path, @NotNull Function<Object, T> parser) {
|
||||||
return stream(path).map(parser);
|
return stream(path).map(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default String childPath(String path) {
|
||||||
|
int index = path.indexOf(pathSeparator());
|
||||||
|
return (index == -1) ? path : path.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
static <T, C extends Collection<T>> @NotNull C parseCollection(
|
static <T, C extends Collection<T>> @NotNull C parseCollection(
|
||||||
@Nullable List<?> data, @NotNull Supplier<C> constructor,
|
@Nullable List<?> data,
|
||||||
|
@NotNull Supplier<C> constructor,
|
||||||
@NotNull DataFunction<Object, T> parser
|
@NotNull DataFunction<Object, T> parser
|
||||||
) {
|
) {
|
||||||
C values = constructor.get();
|
C values = constructor.get();
|
||||||
|
|||||||
@@ -1,12 +1,21 @@
|
|||||||
package cc.carm.lib.configuration.source.section;
|
package cc.carm.lib.configuration.source.section;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.*;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConfigureSource represents the source of configuration,
|
||||||
|
* which can be a file, a database, or any other source.
|
||||||
|
*
|
||||||
|
* @param <SECTION> The type of the root section.
|
||||||
|
* @param <ORIGINAL> The original configuration object.
|
||||||
|
* @param <SELF> The type of the source itself, for further implement support.
|
||||||
|
* @see ConfigureSection
|
||||||
|
*/
|
||||||
public abstract class ConfigureSource<
|
public abstract class ConfigureSource<
|
||||||
SECTION extends ConfigureSection, ORIGINAL,
|
SECTION extends ConfigureSection, ORIGINAL,
|
||||||
SELF extends ConfigureSource<SECTION, ORIGINAL, SELF>>
|
SELF extends ConfigureSource<SECTION, ORIGINAL, SELF>>
|
||||||
@@ -24,28 +33,46 @@ public abstract class ConfigureSource<
|
|||||||
return holder;
|
return holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void reload() throws Exception {
|
public void reload() throws Exception {
|
||||||
onReload(); // 调用重写的Reload方法
|
onReload(); // 调用重写的Reload方法
|
||||||
this.lastUpdateMillis = System.currentTimeMillis();
|
this.lastUpdateMillis = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract SELF self();
|
@Contract(pure = true)
|
||||||
|
@ApiStatus.Internal
|
||||||
|
protected abstract @NotNull SELF self();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Original configuration object
|
* @return The original configuration object.
|
||||||
*/
|
*/
|
||||||
|
@Contract(pure = true)
|
||||||
public abstract @NotNull ORIGINAL original();
|
public abstract @NotNull ORIGINAL original();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The root {@link ConfigureSection}
|
* @return The root {@link ConfigureSection}, which represents the entire configuration.
|
||||||
*/
|
*/
|
||||||
public abstract @NotNull SECTION section();
|
public abstract @NotNull SECTION section();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the whole configuration.
|
||||||
|
*
|
||||||
|
* @throws Exception If any error occurs while saving.
|
||||||
|
*/
|
||||||
public abstract void save() throws Exception;
|
public abstract void save() throws Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload the configuration.
|
||||||
|
* <br>This used for implementation, for external usage, use {@link #reload()}
|
||||||
|
*
|
||||||
|
* @throws Exception If any error occurs while reloading.
|
||||||
|
*/
|
||||||
|
@ApiStatus.OverrideOnly
|
||||||
protected abstract void onReload() throws Exception;
|
protected abstract void onReload() throws Exception;
|
||||||
|
|
||||||
|
public char pathSeparator() {
|
||||||
|
return holder().options().get(StandardOptions.PATH_SEPARATOR);
|
||||||
|
}
|
||||||
|
|
||||||
public long getLastUpdateMillis() {
|
public long getLastUpdateMillis() {
|
||||||
return this.lastUpdateMillis;
|
return this.lastUpdateMillis;
|
||||||
}
|
}
|
||||||
@@ -54,14 +81,20 @@ public abstract class ConfigureSource<
|
|||||||
return getLastUpdateMillis() > parsedTime;
|
return getLastUpdateMillis() > parsedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Source also represents the root section, so it has no parent
|
||||||
|
*
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
@Contract(pure = true, value = "->null")
|
||||||
public @Nullable ConfigureSection parent() {
|
public @Nullable ConfigureSection parent() {
|
||||||
return null; // Source also represents the root section, so it has no parent
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull ConfigureSource<?, ?, ?> source() {
|
public @NotNull String path() {
|
||||||
return self();
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,34 +102,29 @@ public abstract class ConfigureSource<
|
|||||||
return section().getValues(deep);
|
return section().getValues(deep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) {
|
||||||
|
return section().getKeys(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Map<String, Object> asMap() {
|
||||||
|
return section().asMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||||
|
return section().createSection(path, data);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void set(@NotNull String path, @Nullable Object value) {
|
public void set(@NotNull String path, @Nullable Object value) {
|
||||||
section().set(path, value);
|
section().set(path, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(@NotNull String path) {
|
public void remove(@NotNull String path) {
|
||||||
return section().contains(path);
|
section().remove(path);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isList(@NotNull String path) {
|
|
||||||
return section().isList(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable List<?> getList(@NotNull String path) {
|
|
||||||
return section().getList(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSection(@NotNull String path) {
|
|
||||||
return section().isSection(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable ConfigureSection getSection(@NotNull String path) {
|
|
||||||
return section().getSection(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ public abstract class ConfigValue<T> extends ValueManifest<T> {
|
|||||||
* @param override Whether to overwrite existing configured value
|
* @param override Whether to overwrite existing configured value
|
||||||
*/
|
*/
|
||||||
public void setDefault(boolean override) {
|
public void setDefault(boolean override) {
|
||||||
if (!override && config().contains(path())) return;
|
if (config().contains(path()) && !override) return; // Skip if the value is already set
|
||||||
set(defaults()); // Set the default value
|
set(defaults()); // Set the default value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import cc.carm.lib.configuration.adapter.ValueType;
|
|||||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -20,7 +21,6 @@ public class ValueManifest<T> {
|
|||||||
|
|
||||||
protected @NotNull Supplier<@Nullable T> defaultSupplier;
|
protected @NotNull Supplier<@Nullable T> defaultSupplier;
|
||||||
|
|
||||||
|
|
||||||
public ValueManifest(@NotNull ValueType<T> type) {
|
public ValueManifest(@NotNull ValueType<T> type) {
|
||||||
this(type, () -> null, EMPTY_INITIALIZER, null, null);
|
this(type, () -> null, EMPTY_INITIALIZER, null, null);
|
||||||
}
|
}
|
||||||
@@ -93,12 +93,12 @@ public class ValueManifest<T> {
|
|||||||
|
|
||||||
public @NotNull String path() {
|
public @NotNull String path() {
|
||||||
if (path != null) return path;
|
if (path != null) return path;
|
||||||
else throw new IllegalStateException("No section path provided.");
|
else throw new IllegalStateException("No section path provided for Value(" + type() + ").");
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull ConfigurationHolder<?> holder() {
|
public @NotNull ConfigurationHolder<?> holder() {
|
||||||
if (this.holder != null) return this.holder;
|
if (this.holder != null) return this.holder;
|
||||||
throw new IllegalStateException("Value does not have a provider.");
|
throw new IllegalStateException("Value(" + type() + ") does not have a provider.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull ConfigureSource<?, ?, ?> config() {
|
public @NotNull ConfigureSource<?, ?, ?> config() {
|
||||||
@@ -109,16 +109,17 @@ public class ValueManifest<T> {
|
|||||||
return holder().metadata(path());
|
return holder().metadata(path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
protected Object getData() {
|
protected Object getData() {
|
||||||
return config().get(path());
|
return config().get(path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
protected void setData(@Nullable Object value) {
|
protected void setData(@Nullable Object value) {
|
||||||
config().set(path(), value);
|
config().set(path(), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, valuePath) -> {
|
||||||
private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, path) -> {
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -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.0</version>
|
<version>4.0.11</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<properties>
|
<properties>
|
||||||
@@ -33,6 +33,13 @@
|
|||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-feature-versioned</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import cc.carm.lib.configuration.value.ConfigValue;
|
|||||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||||
|
|
||||||
@HeaderComments({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
|
@HeaderComments({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
|
||||||
public class DatabaseConfiguration implements Configuration {
|
public interface DatabaseConfiguration extends Configuration {
|
||||||
|
|
||||||
@ConfigPath("driver")
|
@ConfigPath("driver")
|
||||||
@HeaderComments({
|
@HeaderComments({
|
||||||
@@ -16,18 +16,19 @@ public class DatabaseConfiguration implements Configuration {
|
|||||||
"- MySQL(新): com.mysql.cj.jdbc.Driver",
|
"- MySQL(新): com.mysql.cj.jdbc.Driver",
|
||||||
"- MariaDB(推荐): org.mariadb.jdbc.Driver",
|
"- MariaDB(推荐): org.mariadb.jdbc.Driver",
|
||||||
})
|
})
|
||||||
protected static final ConfigValue<String> DRIVER_NAME = ConfiguredValue.of(
|
ConfigValue<String> DRIVER_NAME = ConfiguredValue.of(
|
||||||
String.class, "com.mysql.cj.jdbc.Driver"
|
String.class, "com.mysql.cj.jdbc.Driver"
|
||||||
);
|
);
|
||||||
|
|
||||||
protected static final ConfigValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
|
ConfigValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
|
||||||
protected static final ConfigValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
|
ConfigValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
|
||||||
protected static final ConfigValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
|
ConfigValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
|
||||||
protected static final ConfigValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
|
ConfigValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
|
||||||
protected static final ConfigValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
|
ConfigValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
|
||||||
protected static final ConfigValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
|
|
||||||
|
|
||||||
protected static String buildJDBC() {
|
ConfigValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
|
||||||
|
|
||||||
|
static String buildJDBC() {
|
||||||
return String.format("jdbc:mysql://%s:%s/%s%s", HOST.get(), PORT.get(), DATABASE.get(), EXTRA.get());
|
return String.format("jdbc:mysql://%s:%s/%s%s", HOST.get(), PORT.get(), DATABASE.get(), EXTRA.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,14 +69,14 @@ public class ConfigurationTest {
|
|||||||
provider.initialize(TEST);
|
provider.initialize(TEST);
|
||||||
|
|
||||||
System.out.println("> Test Inner value before:");
|
System.out.println("> Test Inner value before:");
|
||||||
System.out.println(TEST.INSTANCE.INNER_VALUE.resolve());
|
System.out.println(TEST.INSTANCE.STATUS.resolve());
|
||||||
|
|
||||||
double after = Math.random() * 200D;
|
double after = Math.random() * 200D;
|
||||||
System.out.println("> Test Inner value -> " + after);
|
System.out.println("> Test Inner value -> " + after);
|
||||||
TEST.INSTANCE.INNER_VALUE.set(after);
|
TEST.INSTANCE.STATUS.set(after);
|
||||||
|
|
||||||
System.out.println("> Test Inner value after:");
|
System.out.println("> Test Inner value after:");
|
||||||
System.out.println(TEST.INSTANCE.INNER_VALUE.resolve());
|
System.out.println(TEST.INSTANCE.STATUS.resolve());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-11
@@ -1,11 +1,7 @@
|
|||||||
package cc.carm.lib.configuration.demo.tests.conf;
|
package cc.carm.lib.configuration.demo.tests.conf;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.Configuration;
|
import cc.carm.lib.configuration.Configuration;
|
||||||
import cc.carm.lib.configuration.annotation.ConfigPath;
|
import cc.carm.lib.configuration.annotation.*;
|
||||||
import cc.carm.lib.configuration.annotation.FooterComments;
|
|
||||||
import cc.carm.lib.configuration.annotation.HeaderComments;
|
|
||||||
import cc.carm.lib.configuration.annotation.InlineComment;
|
|
||||||
import cc.carm.lib.configuration.demo.DatabaseConfiguration;
|
|
||||||
import cc.carm.lib.configuration.demo.tests.model.ItemStack;
|
import cc.carm.lib.configuration.demo.tests.model.ItemStack;
|
||||||
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
|
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
|
||||||
import cc.carm.lib.configuration.value.ConfigValue;
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
@@ -29,7 +25,8 @@ import java.util.UUID;
|
|||||||
public interface DemoConfiguration extends Configuration {
|
public interface DemoConfiguration extends Configuration {
|
||||||
|
|
||||||
@ConfigPath(root = true)
|
@ConfigPath(root = true)
|
||||||
ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
|
@ConfigVersion(2)
|
||||||
|
ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 2.0D);
|
||||||
|
|
||||||
@ConfigPath(root = true)
|
@ConfigPath(root = true)
|
||||||
@FooterComments({"此处内容将显示在配置条目的下方", "可用于补充说明,但一般不建议使用"})
|
@FooterComments({"此处内容将显示在配置条目的下方", "可用于补充说明,但一般不建议使用"})
|
||||||
@@ -39,15 +36,15 @@ 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);
|
||||||
|
|
||||||
// 支持通过 Class<?> 变量标注子配置,一并注册。
|
@HeaderComments({"空值测试"})
|
||||||
// 注意: 若对应类也有注解,则优先使用类的注解。
|
@InlineComment("空值Inline注释")
|
||||||
Class<?> DATABASE = DatabaseConfiguration.class;
|
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的首行末尾") // 通过注解给配置添加注释。
|
||||||
@InlineComment(value = "用户名(匹配注释)", regex = "name") // 通过注解给配置添加注释。
|
|
||||||
@InlineComment(value = "信息", regex = "info.*") // 通过注解给配置添加注释。
|
|
||||||
ConfiguredList<UserRecord> ALLOWLISTS = ConfiguredList.builderOf(UserRecord.class).fromSection()
|
ConfiguredList<UserRecord> ALLOWLISTS = ConfiguredList.builderOf(UserRecord.class).fromSection()
|
||||||
.parse(UserRecord::deserialize).serialize(UserRecord::serialize)
|
.parse(UserRecord::deserialize).serialize(UserRecord::serialize)
|
||||||
.defaults(UserRecord.CARM).build();
|
.defaults(UserRecord.CARM).build();
|
||||||
@@ -81,6 +78,7 @@ public interface DemoConfiguration extends Configuration {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
@HeaderComments({"内部类的内部类测试", "通过这种方式,您可以轻易实现多层次的配置文件结构"})
|
@HeaderComments({"内部类的内部类测试", "通过这种方式,您可以轻易实现多层次的配置文件结构"})
|
||||||
|
@FooterComments({"-------------"})
|
||||||
public interface That extends Configuration {
|
public interface That extends Configuration {
|
||||||
|
|
||||||
ConfiguredList<UUID> OPERATORS = ConfiguredList
|
ConfiguredList<UUID> OPERATORS = ConfiguredList
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
|||||||
@HeaderComments("Inner Test")
|
@HeaderComments("Inner Test")
|
||||||
public class InstanceConfig implements Configuration {
|
public class InstanceConfig implements Configuration {
|
||||||
|
|
||||||
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(1.0D);
|
public final ConfigValue<Double> STATUS = ConfiguredValue.of(1.0D);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ public class RegistryConfig implements Configuration {
|
|||||||
|
|
||||||
@ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
@ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||||
@FooterComments({"12313213212"})
|
@FooterComments({"12313213212"})
|
||||||
public final ConfigValue<UserRecord> TEST_MODEL = ConfiguredValue.builderOf(UserRecord.class).fromSection()
|
@InlineComment(value = "用户名(匹配注释)", regex = "name") // 通过注解给配置添加注释。
|
||||||
|
@InlineComment(value = "信息", regex = {"info.*", "info.game.*"}) // 通过注解给配置添加注释。
|
||||||
|
public final ConfigValue<UserRecord> OWNER = ConfiguredValue.builderOf(UserRecord.class).fromSection()
|
||||||
.defaults(new UserRecord("Carm", UUID.randomUUID()))
|
.defaults(new UserRecord("Carm", UUID.randomUUID()))
|
||||||
.parse((holder, section) -> UserRecord.deserialize(section))
|
.parse((holder, section) -> UserRecord.deserialize(section))
|
||||||
.serialize((holder, data) -> data.serialize()).build();
|
.serialize((holder, data) -> data.serialize()).build();
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public class UserRecord extends AbstractRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static UserRecord deserialize(ConfigureSection section) {
|
public static UserRecord deserialize(ConfigureSection section) {
|
||||||
|
System.out.println("> Deserializing -> " + section.fullPath());
|
||||||
String name = section.getString("name");
|
String name = section.getString("name");
|
||||||
if (name == null) throw new NullPointerException("name is null");
|
if (name == null) throw new NullPointerException("name is null");
|
||||||
String uuidString = section.getString("info.uuid");
|
String uuidString = section.getString("info.uuid");
|
||||||
|
|||||||
@@ -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.0</version>
|
<version>4.0.11</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
+4
-1
@@ -33,7 +33,7 @@ public @interface InlineComment {
|
|||||||
* If the regex is not empty, the comment will be added to
|
* If the regex is not empty, the comment will be added to
|
||||||
* all sub paths if the regex matches the value.
|
* all sub paths if the regex matches the value.
|
||||||
* If the regex is empty, the comment will be added to the current path.
|
* If the regex is empty, the comment will be added to the current path.
|
||||||
* <p> e.g. for section, set <b>{"^foo", "*", "bar"}</b> will be set like
|
* <p> e.g. for section, set <b>"foo.*.bar"</b> will be set like
|
||||||
* <blockquote><pre>
|
* <blockquote><pre>
|
||||||
* section:
|
* section:
|
||||||
* foo:
|
* foo:
|
||||||
@@ -44,6 +44,9 @@ public @interface InlineComment {
|
|||||||
* bar: "foobar" # Comment Contents
|
* bar: "foobar" # Comment Contents
|
||||||
* </pre></blockquote>
|
* </pre></blockquote>
|
||||||
*
|
*
|
||||||
|
* <p><i>Some implements may not support this feature
|
||||||
|
* in list values or other complex structures.</i>
|
||||||
|
*
|
||||||
* @return The path regexes of this comment
|
* @return The path regexes of this comment
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
+6
-1
@@ -9,7 +9,12 @@ import java.lang.annotation.Target;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface InlineComments {
|
public @interface InlineComments {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiple inline comments support.
|
||||||
|
*
|
||||||
|
* @return inline comment contents.
|
||||||
|
* @see InlineComment
|
||||||
|
*/
|
||||||
InlineComment[] value();
|
InlineComment[] value();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
package cc.carm.lib.configuration.commentable;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||||
|
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Commentable {
|
||||||
|
|
||||||
|
private Commentable() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerMeta(@NotNull ConfigurationInitializer initializer) {
|
||||||
|
CommentableMeta.register(initializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable String getInlineComment(@NotNull ConfigurationHolder<?> holder, @NotNull String path) {
|
||||||
|
String comment = getInlineComment(holder, path, null);
|
||||||
|
if (comment != null) return comment;
|
||||||
|
|
||||||
|
String sep = String.valueOf(holder.option(StandardOptions.PATH_SEPARATOR));
|
||||||
|
|
||||||
|
// If the comment is not found, try to get the comment from the parent section
|
||||||
|
String[] keys = path.split(Pattern.quote(sep));
|
||||||
|
if (keys.length == 1) return null;
|
||||||
|
|
||||||
|
// Try every possible parent key&child key combination
|
||||||
|
for (int i = 1; i < keys.length; i++) {
|
||||||
|
String parentKey = String.join(sep, Arrays.copyOfRange(keys, 0, i));
|
||||||
|
String childKey = String.join(sep, Arrays.copyOfRange(keys, i, keys.length));
|
||||||
|
comment = getInlineComment(holder, parentKey, childKey);
|
||||||
|
if (comment != null) return comment;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable String getInlineComment(@NotNull ConfigurationHolder<?> holder, @NotNull String path, @Nullable String sectionKey) {
|
||||||
|
Map<String, String> pathComment = holder.metadata(path).get(CommentableMeta.INLINE);
|
||||||
|
if (pathComment == null || pathComment.isEmpty()) return null;
|
||||||
|
if (sectionKey == null) return pathComment.get(null);
|
||||||
|
for (Map.Entry<String/*regex*/, String/*content*/> entry : pathComment.entrySet()) {
|
||||||
|
if (entry.getKey() == null) continue;
|
||||||
|
if (Objects.equals(entry.getKey(), sectionKey)) return entry.getValue();
|
||||||
|
Pattern pattern = Pattern.compile(entry.getKey().replace(".", "\\.").replace("*", "(.*)"));
|
||||||
|
if (pattern.matcher(sectionKey).matches()) return entry.getValue();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable List<String> getHeaderComments(@NotNull ConfigurationHolder<?> holder, @Nullable String path) {
|
||||||
|
return holder.metadata(path).get(CommentableMeta.HEADER);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable List<String> getFooterComments(@NotNull ConfigurationHolder<?> holder, @Nullable String path) {
|
||||||
|
return holder.metadata(path).get(CommentableMeta.FOOTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+11
@@ -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
@@ -1,4 +1,4 @@
|
|||||||
package cc.carm.lib.configuration.option;
|
package cc.carm.lib.configuration.commentable;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||||
|
|
||||||
@@ -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.0</version>
|
<version>4.0.11</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -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.0</version>
|
<version>4.0.11</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
+189
@@ -0,0 +1,189 @@
|
|||||||
|
package cc.carm.lib.configuration.source.section;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.annotations.UnmodifiableView;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public abstract class AbstractMapSection<R extends AbstractMapSection<R>> implements ConfigureSection {
|
||||||
|
|
||||||
|
protected final @NotNull Map<String, Object> data;
|
||||||
|
protected final @Nullable R parent;
|
||||||
|
protected final @NotNull String path;
|
||||||
|
|
||||||
|
protected AbstractMapSection(@Nullable R parent, @NotNull String path) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.path = path;
|
||||||
|
this.data = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void migrate(Map<?, ?> data) {
|
||||||
|
for (Map.Entry<?, ?> entry : data.entrySet()) {
|
||||||
|
String key = (entry.getKey() == null) ? "" : entry.getKey().toString();
|
||||||
|
if (entry.getValue() instanceof Map) {
|
||||||
|
this.data.put(key, createSection(key, (Map<?, ?>) entry.getValue()));
|
||||||
|
} else if (entry.getValue() instanceof List) {
|
||||||
|
List<Object> list = new ArrayList<>();
|
||||||
|
int index = 0;
|
||||||
|
for (Object obj : (List<?>) entry.getValue()) {
|
||||||
|
if (obj instanceof Map) {
|
||||||
|
list.add(createSection(key + "[" + index + "]", (Map<?, ?>) obj));
|
||||||
|
} else {
|
||||||
|
list.add(obj);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
this.data.put(key, list);
|
||||||
|
} else {
|
||||||
|
this.data.put(key, entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract @NotNull R self();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract @NotNull R createSection(@NotNull String path, @NotNull Map<?, ?> data);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String path() {
|
||||||
|
return this.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@NotNull String path) {
|
||||||
|
R section = getSectionFor(path);
|
||||||
|
if (section == this) {
|
||||||
|
return this.data().containsKey(path);
|
||||||
|
} else {
|
||||||
|
return section.contains(childPath(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Map<String, Object> data() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.data.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size(boolean deep) {
|
||||||
|
return deep ? getKeys(true).size() : this.data.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@UnmodifiableView
|
||||||
|
public @NotNull Map<String, Object> asMap() {
|
||||||
|
Map<String, Object> output = new LinkedHashMap<>();
|
||||||
|
for (Map.Entry<String, Object> entry : this.data.entrySet()) {
|
||||||
|
if (entry.getValue() instanceof AbstractMapSection<?>) {
|
||||||
|
output.put(entry.getKey(), ((AbstractMapSection<?>) entry.getValue()).asMap());
|
||||||
|
} else if (entry.getValue() instanceof List<?>) {
|
||||||
|
List<Object> list = new ArrayList<>();
|
||||||
|
for (Object obj : (List<?>) entry.getValue()) {
|
||||||
|
if (obj instanceof AbstractMapSection<?>) {
|
||||||
|
list.add(((AbstractMapSection<?>) obj).asMap());
|
||||||
|
} else {
|
||||||
|
list.add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output.put(entry.getKey(), list);
|
||||||
|
} else {
|
||||||
|
output.put(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable R parent() {
|
||||||
|
return this.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||||
|
return Collections.unmodifiableMap(deep ? mappingValues(this, null, true, String.valueOf(pathSeparator())) : data());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) {
|
||||||
|
return Collections.unmodifiableSet(deep ? mappingKeys(this, null, true, String.valueOf(pathSeparator())) : data().keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@NotNull String path, @Nullable Object value) {
|
||||||
|
if (value instanceof Map) value = createSection(path, (Map<?, ?>) value);
|
||||||
|
|
||||||
|
R section = getSectionFor(path);
|
||||||
|
if (section == this) {
|
||||||
|
// Even this value is null, we still need to put it in the map
|
||||||
|
// to ensure that the path is marked as existing.
|
||||||
|
this.data.put(path, value);
|
||||||
|
} else {
|
||||||
|
section.set(childPath(path), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(@NotNull String path) {
|
||||||
|
R section = getSectionFor(path);
|
||||||
|
if (section != this) {
|
||||||
|
section.remove(childPath(path));
|
||||||
|
} else {
|
||||||
|
this.data.remove(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Object get(@NotNull String path) {
|
||||||
|
R section = getSectionFor(path);
|
||||||
|
return section == this ? data.get(path) : section.get(childPath(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private R getSectionFor(String path) {
|
||||||
|
int index = path.indexOf(pathSeparator());
|
||||||
|
if (index == -1) return self();
|
||||||
|
|
||||||
|
String root = path.substring(0, index);
|
||||||
|
return (R) computeSection(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map the values of the children of the section to the output map.
|
||||||
|
*
|
||||||
|
* @param section The section to map the values from
|
||||||
|
* @param parent The parent path
|
||||||
|
* @param deep If the mapping should be deep
|
||||||
|
*/
|
||||||
|
protected static Map<String, Object> mappingValues(@NotNull AbstractMapSection<?> section, @Nullable String parent, boolean deep, String pathSeparator) {
|
||||||
|
Map<String, Object> output = new LinkedHashMap<>();
|
||||||
|
for (Map.Entry<String, Object> entry : section.data().entrySet()) {
|
||||||
|
String path = (parent == null ? "" : parent + pathSeparator) + entry.getKey();
|
||||||
|
output.remove(path);
|
||||||
|
output.put(path, entry.getValue());
|
||||||
|
if (deep && entry.getValue() instanceof AbstractMapSection<?>) {
|
||||||
|
output.putAll(mappingValues((AbstractMapSection<?>) entry.getValue(), path, true, pathSeparator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Set<String> mappingKeys(@NotNull AbstractMapSection<?> section, @Nullable String parent, boolean deep, String pathSeparator) {
|
||||||
|
Set<String> keys = new LinkedHashSet<>();
|
||||||
|
for (Map.Entry<String, Object> entry : section.data().entrySet()) {
|
||||||
|
String path = (parent == null ? "" : parent + pathSeparator) + entry.getKey();
|
||||||
|
keys.add(path);
|
||||||
|
if (deep && entry.getValue() instanceof AbstractMapSection<?>) {
|
||||||
|
keys.addAll(mappingKeys((AbstractMapSection<?>) entry.getValue(), path, true, pathSeparator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+383
@@ -0,0 +1,383 @@
|
|||||||
|
package cc.carm.lib.configuration.source.section;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.annotations.UnmodifiableView;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class ImmutableSection implements ConfigureSection {
|
||||||
|
|
||||||
|
public static ImmutableSection of(@NotNull ConfigureSection section) {
|
||||||
|
if (section instanceof ImmutableSection) {
|
||||||
|
return (ImmutableSection) section;
|
||||||
|
} else return new ImmutableSection(null, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final @Nullable ImmutableSection parent;
|
||||||
|
protected final @NotNull ConfigureSection raw;
|
||||||
|
|
||||||
|
private ImmutableSection(@Nullable ImmutableSection parent, @NotNull ConfigureSection raw) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.raw = raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull ConfigureSection raw() {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ImmutableSection parent() {
|
||||||
|
return this.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String path() {
|
||||||
|
return raw().path();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Map<String, Object> getValues(boolean deep) {
|
||||||
|
return raw().getValues(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@NotNull String path, @Nullable Object value) {
|
||||||
|
throw new IllegalStateException("This section is not modifiable!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(@NotNull String path) {
|
||||||
|
throw new IllegalStateException("This section is not modifiable!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ImmutableSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||||
|
return new ImmutableSection(this, raw().createSection(path, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Object get(@NotNull String path) {
|
||||||
|
Object value = raw().get(path);
|
||||||
|
if (value instanceof ConfigureSection && !(value instanceof ImmutableSection)) {
|
||||||
|
return new ImmutableSection(this, (ConfigureSection) value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigureSection getSection(@NotNull String path) {
|
||||||
|
ConfigureSection get = raw().getSection(path);
|
||||||
|
if (get != null && !(get instanceof ImmutableSection)) {
|
||||||
|
return new ImmutableSection(this, get);
|
||||||
|
}
|
||||||
|
return get;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char pathSeparator() {
|
||||||
|
return raw().pathSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRoot() {
|
||||||
|
return raw().isRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return raw().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) {
|
||||||
|
return raw().getKeys(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Set<String> keys() {
|
||||||
|
return raw().keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Map<String, Object> values() {
|
||||||
|
return raw().values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Map<String, Object> asMap() {
|
||||||
|
return raw().asMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<Map.Entry<String, Object>> stream() {
|
||||||
|
return raw().stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEach(@NotNull BiConsumer<String, Object> action) {
|
||||||
|
raw().forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(@NotNull String path) {
|
||||||
|
return raw().contains(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(@NotNull String path) {
|
||||||
|
return raw().containsValue(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
|
||||||
|
return raw().isType(path, typeClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isList(@NotNull String path) {
|
||||||
|
return raw().isList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable List<?> getList(@NotNull String path) {
|
||||||
|
return raw().getList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSection(@NotNull String path) {
|
||||||
|
return raw().isSection(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> @Nullable T get(@NotNull String path, @NotNull Class<T> type) {
|
||||||
|
return raw().get(path, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> @Nullable T get(@NotNull String path, @NotNull DataFunction<@Nullable Object, T> parser) {
|
||||||
|
return raw().get(path, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> @Nullable T get(@NotNull String path, @Nullable T defaults, @NotNull Class<T> clazz) {
|
||||||
|
return raw().get(path, defaults, clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> @Nullable T get(@NotNull String path, @Nullable T defaultValue, @NotNull DataFunction<Object, T> parser) {
|
||||||
|
return raw().get(path, defaultValue, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isBoolean(@NotNull String path) {
|
||||||
|
return raw().isBoolean(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBoolean(@NotNull String path) {
|
||||||
|
return raw().getBoolean(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
|
||||||
|
return raw().getBoolean(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Boolean isByte(@NotNull String path) {
|
||||||
|
return raw().isByte(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Byte getByte(@NotNull String path) {
|
||||||
|
return raw().getByte(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
|
||||||
|
return raw().getByte(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isShort(@NotNull String path) {
|
||||||
|
return raw().isShort(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Short getShort(@NotNull String path) {
|
||||||
|
return raw().getShort(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
|
||||||
|
return raw().getShort(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInt(@NotNull String path) {
|
||||||
|
return raw().isInt(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Integer getInt(@NotNull String path) {
|
||||||
|
return raw().getInt(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
|
||||||
|
return raw().getInt(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLong(@NotNull String path) {
|
||||||
|
return raw().isLong(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Long getLong(@NotNull String path) {
|
||||||
|
return raw().getLong(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
|
||||||
|
return raw().getLong(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFloat(@NotNull String path) {
|
||||||
|
return raw().isFloat(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Float getFloat(@NotNull String path) {
|
||||||
|
return raw().getFloat(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
|
||||||
|
return raw().getFloat(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDouble(@NotNull String path) {
|
||||||
|
return raw().isDouble(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Double getDouble(@NotNull String path) {
|
||||||
|
return raw().getDouble(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
|
||||||
|
return raw().getDouble(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChar(@NotNull String path) {
|
||||||
|
return raw().isChar(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Character getChar(@NotNull String path) {
|
||||||
|
return raw().getChar(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
|
||||||
|
return raw().getChar(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isString(@NotNull String path) {
|
||||||
|
return raw().isString(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getString(@NotNull String path) {
|
||||||
|
return raw().getString(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String getString(@NotNull String path, @Nullable String def) {
|
||||||
|
return raw().getString(path, def);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull <V> List<V> getList(@NotNull String path, @NotNull DataFunction<Object, V> parser) {
|
||||||
|
return raw().getList(path, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<String> getStringList(@NotNull String path) {
|
||||||
|
return raw().getStringList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<Integer> getIntegerList(@NotNull String path) {
|
||||||
|
return raw().getIntegerList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<Long> getLongList(@NotNull String path) {
|
||||||
|
return raw().getLongList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<Double> getDoubleList(@NotNull String path) {
|
||||||
|
return raw().getDoubleList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<Float> getFloatList(@NotNull String path) {
|
||||||
|
return raw().getFloatList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<Byte> getByteList(@NotNull String path) {
|
||||||
|
return raw().getByteList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull List<Character> getCharList(@NotNull String path) {
|
||||||
|
return raw().getCharList(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path, @NotNull Supplier<C> constructor, @NotNull DataFunction<Object, T> parser) {
|
||||||
|
return raw().getCollection(path, constructor, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Stream<?> stream(@NotNull String path) {
|
||||||
|
return raw().stream(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull <T> Stream<T> stream(@NotNull String path, @NotNull Function<Object, T> parser) {
|
||||||
|
return raw().stream(path, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String childPath(String path) {
|
||||||
|
return raw().childPath(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return raw.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return Objects.equals(raw, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+32
-126
@@ -3,148 +3,54 @@ package cc.carm.lib.configuration.source.section;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class MemorySection implements ConfigureSection {
|
public class MemorySection extends AbstractMapSection<MemorySection> {
|
||||||
|
|
||||||
public static @NotNull MemorySection root(@NotNull ConfigureSource<? extends MemorySection, ?, ?> source) {
|
public static MemorySection of() {
|
||||||
return new MemorySection(source, new LinkedHashMap<>(), null);
|
return of(new LinkedHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull MemorySection root(@NotNull ConfigureSource<? extends MemorySection, ?, ?> source,
|
public static MemorySection of(@NotNull Consumer<Map<String, Object>> data) {
|
||||||
@Nullable Map<?, ?> data) {
|
return of(() -> {
|
||||||
return new MemorySection(source, data == null ? new LinkedHashMap<>() : data, null);
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
data.accept(map);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final @NotNull ConfigureSource<? extends MemorySection, ?, ?> source;
|
public static MemorySection of(@NotNull Supplier<Map<?, ?>> data) {
|
||||||
protected final @NotNull Map<String, Object> data;
|
return of(data.get());
|
||||||
protected final @Nullable MemorySection parent;
|
|
||||||
|
|
||||||
public MemorySection(@NotNull ConfigureSource<? extends MemorySection, ?, ?> source,
|
|
||||||
@NotNull Map<?, ?> data, @Nullable MemorySection 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, createChild((Map<?, ?>) entry.getValue()));
|
|
||||||
} else if (entry.getValue() instanceof List) {
|
|
||||||
List<Object> list = new ArrayList<>();
|
|
||||||
for (Object obj : (List<?>) entry.getValue()) {
|
|
||||||
if (obj instanceof Map) {
|
|
||||||
list.add(createChild((Map<?, ?>) obj));
|
|
||||||
} else {
|
|
||||||
list.add(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.data.put(key, list);
|
|
||||||
} else {
|
|
||||||
this.data.put(key, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected @NotNull MemorySection createChild(@NotNull Map<?, ?> data) {
|
public static MemorySection of(@NotNull Map<?, ?> data) {
|
||||||
return new MemorySection(source(), data, this);
|
return of(data, null, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected @NotNull MemorySection createChild() {
|
public static MemorySection of(@Nullable MemorySection parent, @NotNull String path) {
|
||||||
return createChild(new LinkedHashMap<>());
|
return of(new LinkedHashMap<>(), parent, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MemorySection of(@NotNull Map<?, ?> data, @Nullable MemorySection parent, @NotNull String path) {
|
||||||
|
return new MemorySection(data, parent, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MemorySection(@NotNull Map<?, ?> raw, @Nullable MemorySection parent, @NotNull String path) {
|
||||||
|
super(parent, path);
|
||||||
|
migrate(raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull ConfigureSource<? extends MemorySection, ?, ?> source() {
|
public @NotNull MemorySection self() {
|
||||||
return this.source;
|
return this;
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull Map<String, Object> data() {
|
|
||||||
return this.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable MemorySection parent() {
|
|
||||||
return this.parent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
public @NotNull MemorySection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||||
return Collections.unmodifiableMap(deep ? mapChildrenValues(this, null, true) : data());
|
return new MemorySection(data, this, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(@NotNull String path, @Nullable Object value) {
|
|
||||||
if (value instanceof Map) value = createChild((Map<?, ?>) value);
|
|
||||||
|
|
||||||
MemorySection section = getSectionFor(path);
|
|
||||||
if (section == this) {
|
|
||||||
// Even this value is null, we still need to put it in the map
|
|
||||||
// to ensure that the path is marked as existing.
|
|
||||||
this.data.put(path, value);
|
|
||||||
} else {
|
|
||||||
section.set(childPath(path), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean contains(@NotNull String path) {
|
|
||||||
return get(path) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable Object get(@NotNull String path) {
|
|
||||||
MemorySection section = getSectionFor(path);
|
|
||||||
return section == this ? data.get(path) : section.get(childPath(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable List<?> getList(@NotNull String path) {
|
|
||||||
Object val = get(path);
|
|
||||||
return (val instanceof List<?>) ? (List<?>) val : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @Nullable ConfigureSection getSection(@NotNull String path) {
|
|
||||||
Object val = get(path);
|
|
||||||
return (val instanceof ConfigureSection) ? (ConfigureSection) val : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MemorySection 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 = createChild();
|
|
||||||
this.data.put(root, section);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (MemorySection) section;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String childPath(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 section The section to map the values from
|
|
||||||
* @param parent The parent path
|
|
||||||
* @param deep If the mapping should be deep
|
|
||||||
*/
|
|
||||||
protected Map<String, Object> mapChildrenValues(@NotNull MemorySection section,
|
|
||||||
@Nullable String parent, boolean deep) {
|
|
||||||
Map<String, Object> output = new LinkedHashMap<>();
|
|
||||||
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 MemorySection) {
|
|
||||||
output.putAll(mapChildrenValues((MemorySection) entry.getValue(), path, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+151
@@ -0,0 +1,151 @@
|
|||||||
|
package cc.carm.lib.configuration.source.section;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.annotations.UnmodifiableView;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ShadedSection implements ConfigureSection {
|
||||||
|
|
||||||
|
public static ShadedSection create(@NotNull ConfigureSection template, @Nullable ConfigureSection source) {
|
||||||
|
return new ShadedSection(null, template, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final @Nullable ShadedSection parent;
|
||||||
|
protected @NotNull ConfigureSection template;
|
||||||
|
protected @Nullable ConfigureSection source;
|
||||||
|
|
||||||
|
public ShadedSection(@Nullable ShadedSection parent,
|
||||||
|
@NotNull ConfigureSection template, @Nullable ConfigureSection source) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.template = template;
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ConfigureSection parent() {
|
||||||
|
return this.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String path() {
|
||||||
|
return this.template.path();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Map<String, Object> getValues(boolean deep) {
|
||||||
|
if (source == null) return template.getValues(deep);
|
||||||
|
// 本函数为,当 getValues 时,递归合并 source 和 template
|
||||||
|
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
|
||||||
|
public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) {
|
||||||
|
Set<String> keys = new HashSet<>(template.getKeys(deep));
|
||||||
|
if (source != null) {
|
||||||
|
keys.addAll(source.getKeys(deep));
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableSet(keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConfigureSection merge(ConfigureSection templateSection, ConfigureSection valueSection) {
|
||||||
|
MemorySection merged = MemorySection.of();
|
||||||
|
Set<String> existingKey = new HashSet<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : valueSection.getValues(false).entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
if (value instanceof ConfigureSection) {
|
||||||
|
ConfigureSection subSectionFromValue = (ConfigureSection) value;
|
||||||
|
ConfigureSection subSectionFromTemplate = (ConfigureSection) templateSection.get(key);
|
||||||
|
if (subSectionFromTemplate == null) {
|
||||||
|
merged.set(key, value);
|
||||||
|
} else {
|
||||||
|
merged.set(key, merge(subSectionFromTemplate, subSectionFromValue));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
merged.set(key, value);
|
||||||
|
}
|
||||||
|
existingKey.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : templateSection.getValues(false).entrySet()) {
|
||||||
|
if (existingKey.contains(entry.getKey())) continue;
|
||||||
|
merged.set(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@NotNull String path, @Nullable Object value) {
|
||||||
|
if (value instanceof ConfigureSection) {
|
||||||
|
ConfigureSection targetSection = (ConfigureSection) value;
|
||||||
|
for (Map.Entry<String, Object> entry : targetSection.getValues(true).entrySet()) {
|
||||||
|
set(path + pathSeparator() + entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (Objects.equals(get(path), value)) {
|
||||||
|
remove(path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional.ofNullable(source).ifPresent(s -> s.set(path, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(@NotNull String path) {
|
||||||
|
Optional.ofNullable(source).ifPresent(s -> s.remove(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||||
|
if (source == null) {
|
||||||
|
return new ShadedSection(this, template, MemorySection.of(data));
|
||||||
|
} else {
|
||||||
|
ConfigureSection section = source.computeSection(path, data);
|
||||||
|
return new ShadedSection(this, template, section);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Object get(@NotNull String path) {
|
||||||
|
if (source == null) {
|
||||||
|
return getFromTemplate(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
Object value = source.get(path);
|
||||||
|
if (value == null) {
|
||||||
|
return getFromTemplate(path);
|
||||||
|
}
|
||||||
|
if (value instanceof ConfigureSection) {
|
||||||
|
ConfigureSection templateSection = (ConfigureSection) template.get(path);
|
||||||
|
if (templateSection == null) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return new ShadedSection(this, templateSection, (ConfigureSection) value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Object getFromTemplate(@NotNull String path) {
|
||||||
|
Object value = template.get(path);
|
||||||
|
if (value instanceof ConfigureSection) {
|
||||||
|
return new ShadedSection(this, (ConfigureSection) value, null);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable ConfigureSection getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package cc.carm.lib.configuration.source.section;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class SourcedSection extends AbstractMapSection<SourcedSection> {
|
||||||
|
|
||||||
|
public static @NotNull SourcedSection root(@NotNull ConfigureSource<? extends SourcedSection, ?, ?> source) {
|
||||||
|
return new SourcedSection(source, new LinkedHashMap<>(), null, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull SourcedSection root(@NotNull ConfigureSource<? extends SourcedSection, ?, ?> source,
|
||||||
|
@Nullable Map<?, ?> raw) {
|
||||||
|
return new SourcedSection(source, raw == null ? new LinkedHashMap<>() : raw, null, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final @NotNull ConfigureSource<? extends SourcedSection, ?, ?> source;
|
||||||
|
|
||||||
|
public SourcedSection(@NotNull ConfigureSource<? extends SourcedSection, ?, ?> source,
|
||||||
|
@NotNull Map<?, ?> raw, @Nullable SourcedSection parent, @NotNull String path) {
|
||||||
|
super(parent, path);
|
||||||
|
this.source = source;
|
||||||
|
migrate(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfigureSource<? extends SourcedSection, ?, ?> source() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char pathSeparator() {
|
||||||
|
return source().pathSeparator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull SourcedSection self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull SourcedSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||||
|
return new SourcedSection(source(), data, this, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package test.section;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
import cc.carm.lib.configuration.source.section.MemorySection;
|
||||||
|
import cc.carm.lib.configuration.source.section.ShadedSection;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lyzen
|
||||||
|
* @date 21/2/2025 下午2:18
|
||||||
|
*/
|
||||||
|
public class ShadeTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
ConfigureSection template = MemorySection.of(data -> {
|
||||||
|
data.put("name", "GentleMan");
|
||||||
|
data.put("age", 12);
|
||||||
|
data.put("gender", "male");
|
||||||
|
Map<String, Object> address = new LinkedHashMap<>();
|
||||||
|
address.put("Hotel", "Nanjing Road 101");
|
||||||
|
address.put("Store", "Beijing Road 404");
|
||||||
|
Map<String, Object> mapInside = new LinkedHashMap<>();
|
||||||
|
mapInside.put("InsideKeyExample", "InsideValueExample");
|
||||||
|
address.put("Inside", mapInside);
|
||||||
|
data.put("addresses", address);
|
||||||
|
data.put("cards", Arrays.asList("00000", "11111", "22222"));
|
||||||
|
});
|
||||||
|
ConfigureSection source = MemorySection.of(data -> {
|
||||||
|
data.put("age", 25);
|
||||||
|
Map<String, Object> address = new LinkedHashMap<>();
|
||||||
|
address.put("NewOne", "Guangdong Road 505");
|
||||||
|
data.put("addresses", address);
|
||||||
|
Map<String, Object> mapInside = new LinkedHashMap<>();
|
||||||
|
mapInside.put("AnotherInsideKey", "AnotherInsideValue");
|
||||||
|
address.put("Inside", mapInside);
|
||||||
|
data.put("cards", Arrays.asList("33333", "55555")); // 应当直接覆盖原先的List
|
||||||
|
});
|
||||||
|
|
||||||
|
ShadedSection root = new ShadedSection(null, template, source);
|
||||||
|
System.out.println("age: " + root.get("age"));
|
||||||
|
System.out.println("addresses: ");
|
||||||
|
for (Map.Entry<String, Object> entry : root.getSection("addresses").getValues(false).entrySet()) {
|
||||||
|
System.out.println(" " + entry.getKey() + ": " + entry.getValue());
|
||||||
|
if (entry.getValue() instanceof ConfigureSection) {
|
||||||
|
for (Map.Entry<String, Object> inner : ((ConfigureSection) entry.getValue()).getValues(false).entrySet()) {
|
||||||
|
System.out.println(" " + inner.getKey() + ": " + inner.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("cards: " + root.getList("cards"));
|
||||||
|
|
||||||
|
System.out.println("\n----------------------\n");
|
||||||
|
System.out.println("Deep Search Test");
|
||||||
|
System.out.println("addresses: ");
|
||||||
|
for (Map.Entry<String, Object> entry : root.getSection("addresses").getValues(true).entrySet()) {
|
||||||
|
System.out.println(" " + entry.getKey() + ": " + entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("\n----------------------\n");
|
||||||
|
|
||||||
|
root.set("addresses", MemorySection.of(map -> {
|
||||||
|
map.put("Hotel", "Nanjing Road 101");
|
||||||
|
map.put("Store", "Beijing Road banned");
|
||||||
|
|
||||||
|
}));
|
||||||
|
|
||||||
|
for (Map.Entry<String, Object> entry : source.getValues(true).entrySet()) {
|
||||||
|
System.out.println(" " + entry.getKey() + ": " + entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?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>4.0.11</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-feature-text</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-gson</artifactId>
|
||||||
|
<version>${project.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,196 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.builder.AbstractConfigBuilder;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
|
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||||
|
import cc.carm.lib.configuration.value.text.data.TextContents;
|
||||||
|
import cc.carm.lib.configuration.value.text.function.TextDispatcher;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <MSG> The type of the message
|
||||||
|
* @param <RECEIVER> The type of the receiver
|
||||||
|
*/
|
||||||
|
public class ConfiguredText<MSG, RECEIVER> extends ConfiguredValue<TextContents> {
|
||||||
|
|
||||||
|
public static <M, R> Builder<M, R, ?> builder() {
|
||||||
|
return new StardardBuilder<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final ValueType<TextContents> TEXT_TYPE = ValueType.of(TextContents.class);
|
||||||
|
public static final ValueAdapter<TextContents> TEXT_ADAPTER = new ValueAdapter<>(TEXT_TYPE,
|
||||||
|
(h, t, d) -> d.serialize(),
|
||||||
|
(h, t, d) -> TextContents.deserialize(d)
|
||||||
|
);
|
||||||
|
|
||||||
|
protected final @NotNull BiFunction<RECEIVER, String, String> parser;
|
||||||
|
protected final @NotNull BiFunction<RECEIVER, String, MSG> compiler;
|
||||||
|
protected final @NotNull BiConsumer<RECEIVER, List<MSG>> dispatcher;
|
||||||
|
|
||||||
|
protected final @NotNull String[] params; // The parameters of the message.
|
||||||
|
|
||||||
|
public ConfiguredText(@NotNull ValueManifest<TextContents> manifest,
|
||||||
|
@NotNull BiFunction<RECEIVER, String, String> parser,
|
||||||
|
@NotNull BiFunction<RECEIVER, String, MSG> compiler,
|
||||||
|
@NotNull BiConsumer<RECEIVER, List<MSG>> dispatcher,
|
||||||
|
@NotNull String[] params) {
|
||||||
|
super(manifest, TEXT_ADAPTER);
|
||||||
|
this.parser = parser;
|
||||||
|
this.compiler = compiler;
|
||||||
|
this.dispatcher = dispatcher;
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextDispatcher<MSG, RECEIVER, ?> prepare(@NotNull Object... values) {
|
||||||
|
return new PreparedText<MSG, RECEIVER>(resolve(), this.params)
|
||||||
|
.parser(this.parser).compiler(this.compiler)
|
||||||
|
.dispatcher(this.dispatcher).placeholders(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the message for the receiver.
|
||||||
|
*
|
||||||
|
* @param receiver The receiver of the message.
|
||||||
|
* @param values The values to replace the {@link #params}.
|
||||||
|
* @return The parsed message.
|
||||||
|
*/
|
||||||
|
public List<String> parse(@Nullable RECEIVER receiver, @NotNull Object... values) {
|
||||||
|
return prepare(values).parse(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the message for the receiver and send it.
|
||||||
|
*
|
||||||
|
* @param receiver The receiver of the message.
|
||||||
|
* @param values The values to replace the {@link #params}.
|
||||||
|
* @return The parsed message.
|
||||||
|
*/
|
||||||
|
public String parseLine(@Nullable RECEIVER receiver, @NotNull Object... values) {
|
||||||
|
return prepare(values).parseLine(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile the message for the receiver.
|
||||||
|
*
|
||||||
|
* @param receiver The receiver of the message.
|
||||||
|
* @param values The values to replace the {@link #params}.
|
||||||
|
* @return The compiled message.
|
||||||
|
*/
|
||||||
|
public List<MSG> compile(@Nullable RECEIVER receiver, @NotNull Object... values) {
|
||||||
|
return prepare(values).compile(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile the message for the receiver and send it.
|
||||||
|
*
|
||||||
|
* @param receiver The receiver of the message.
|
||||||
|
* @param values The values to replace the {@link #params}.
|
||||||
|
* @return The compiled message.
|
||||||
|
*/
|
||||||
|
public MSG compileLine(@Nullable RECEIVER receiver, @NotNull Object... values) {
|
||||||
|
return prepare(values).compileLine(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the message to the receiver.
|
||||||
|
*
|
||||||
|
* @param receiver The receiver of the message.
|
||||||
|
* @param values The values to replace the {@link #params}.
|
||||||
|
*/
|
||||||
|
public void sendTo(@NotNull RECEIVER receiver, @NotNull Object... values) {
|
||||||
|
prepare(values).to(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the message to the multiple receivers.
|
||||||
|
*
|
||||||
|
* @param receivers The receivers of the message.
|
||||||
|
* @param values The values to replace the {@link #params}.
|
||||||
|
*/
|
||||||
|
public void sendToAll(@NotNull Iterable<? extends RECEIVER> receivers, @NotNull Object... values) {
|
||||||
|
prepare(values).to(receivers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class Builder<MSG, RECEIVER, SELF extends Builder<MSG, RECEIVER, SELF>>
|
||||||
|
extends AbstractConfigBuilder<TextContents, ConfiguredText<MSG, RECEIVER>, ConfigurationHolder<?>, SELF> {
|
||||||
|
protected @NotNull TextContents.Builder defaultBuilder = TextContents.builder();
|
||||||
|
protected @NotNull String[] params = new String[0];
|
||||||
|
|
||||||
|
protected @NotNull BiFunction<RECEIVER, String, String> parser = (r, s) -> s;
|
||||||
|
protected @NotNull BiFunction<RECEIVER, String, MSG> compiler = (r, s) -> {
|
||||||
|
throw new IllegalStateException("Compiler not supplied.");
|
||||||
|
};
|
||||||
|
protected @NotNull BiConsumer<RECEIVER, List<MSG>> dispatcher = (r, l) -> {
|
||||||
|
throw new IllegalStateException("Dispatcher not supplied.");
|
||||||
|
};
|
||||||
|
|
||||||
|
protected Builder() {
|
||||||
|
super(ConfigurationHolder.class, TEXT_TYPE);
|
||||||
|
defaults(() -> defaultBuilder.build()); // Set the default value from the default builder.
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF parser(@NotNull BiFunction<RECEIVER, String, String> parser) {
|
||||||
|
this.parser = parser;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF compiler(@NotNull BiFunction<RECEIVER, String, MSG> compiler) {
|
||||||
|
this.compiler = compiler;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF dispatcher(@NotNull BiConsumer<RECEIVER, List<MSG>> dispatcher) {
|
||||||
|
this.dispatcher = dispatcher;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF params(@NotNull String... params) {
|
||||||
|
this.params = params;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF defaults(@NotNull Consumer<TextContents.Builder> consumer) {
|
||||||
|
consumer.accept(this.defaultBuilder);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF defaults(@NotNull String... contents) {
|
||||||
|
return defaults(builder -> builder.set(contents));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF defaults(@NotNull Iterable<String> contents) {
|
||||||
|
return defaults(builder -> builder.set(contents));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF optional(@NotNull String id, @NotNull String... lines) {
|
||||||
|
return defaults(builder -> builder.optional(id, lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF optional(@NotNull String id, @NotNull Iterable<String> lines) {
|
||||||
|
return defaults(builder -> builder.optional(id, lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfiguredText<MSG, RECEIVER> build() {
|
||||||
|
return new ConfiguredText<>(buildManifest(), this.parser, this.compiler, this.dispatcher, this.params);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StardardBuilder<MSG, RECEIVER> extends Builder<MSG, RECEIVER, StardardBuilder<MSG, RECEIVER>> {
|
||||||
|
@Override
|
||||||
|
protected @NotNull StardardBuilder<MSG, RECEIVER> self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.value.text.data.TextContents;
|
||||||
|
import cc.carm.lib.configuration.value.text.function.TextDispatcher;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
public class PreparedText<MSG, RECEIVER> extends TextDispatcher<MSG, RECEIVER, PreparedText<MSG, RECEIVER>> {
|
||||||
|
|
||||||
|
protected final @NotNull TextContents texts;
|
||||||
|
|
||||||
|
public PreparedText(@NotNull TextContents texts, @NotNull String... params) {
|
||||||
|
this.params = params;
|
||||||
|
this.texts = texts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TextContents texts() {
|
||||||
|
return this.texts;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PreparedText<MSG, RECEIVER> self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreparedText<MSG, RECEIVER> insert(@NotNull String key,
|
||||||
|
@NotNull ConfiguredText<?, RECEIVER> message,
|
||||||
|
@NotNull Object... values) {
|
||||||
|
return insert(key, receiver -> message.parse(receiver, values));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+160
@@ -0,0 +1,160 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text.data;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class TextContents {
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextContents of(@NotNull List<String> lines, @NotNull Map<String, List<String>> optional) {
|
||||||
|
return new TextContents(lines, optional);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final @NotNull List<String> lines;
|
||||||
|
protected final @NotNull Map<String, List<String>> optional;
|
||||||
|
|
||||||
|
public TextContents(@NotNull List<String> lines, @NotNull Map<String, List<String>> optional) {
|
||||||
|
this.lines = lines;
|
||||||
|
this.optional = optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return lines.isEmpty() || lines.stream().allMatch(String::isEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull List<String> lines() {
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull Map<String, List<String>> optionalLines() {
|
||||||
|
return optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable Object serialize() {
|
||||||
|
if (optional.isEmpty()) {
|
||||||
|
if (lines.isEmpty()) return null;
|
||||||
|
else if (lines.size() == 1) return lines.get(0);
|
||||||
|
else return lines;
|
||||||
|
} else {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("contents", lines);
|
||||||
|
map.put("optional", optional);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable TextContents deserialize(@NotNull Object data) {
|
||||||
|
Builder builder = builder();
|
||||||
|
if (data instanceof String) {
|
||||||
|
return builder.set((String) data).build();
|
||||||
|
} else if (data instanceof List<?>) {
|
||||||
|
((List<?>) data).stream().map(Object::toString).forEach(builder::add);
|
||||||
|
return builder.build();
|
||||||
|
} else if (data instanceof ConfigureSection) {
|
||||||
|
ConfigureSection section = (ConfigureSection) data;
|
||||||
|
builder.set(section.getStringList("contents"));
|
||||||
|
|
||||||
|
ConfigureSection optionalSection = section.getSection("optional");
|
||||||
|
if (optionalSection != null) {
|
||||||
|
for (String key : optionalSection.getKeys(false)) {
|
||||||
|
builder.optional(key, optionalSection.getStringList(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
protected List<String> lines = new ArrayList<>();
|
||||||
|
protected Map<String, List<String>> optional = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add lines to the contents
|
||||||
|
*
|
||||||
|
* @param lines lines to add
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public Builder add(@NotNull String... lines) {
|
||||||
|
this.lines.addAll(Arrays.asList(lines));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add lines to the contents
|
||||||
|
*
|
||||||
|
* @param lines lines to add
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public Builder add(@NotNull Iterable<String> lines) {
|
||||||
|
lines.forEach(this.lines::add);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the lines of the contents
|
||||||
|
*
|
||||||
|
* @param lines lines to set
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public Builder set(@NotNull String... lines) {
|
||||||
|
this.lines = Arrays.asList(lines);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the lines of the contents
|
||||||
|
*
|
||||||
|
* @param lines lines to set
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public Builder set(@NotNull Iterable<String> lines) {
|
||||||
|
this.lines = new ArrayList<>();
|
||||||
|
lines.forEach(this.lines::add);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add optional lines to the contents
|
||||||
|
*
|
||||||
|
* @param key key of the optional lines
|
||||||
|
* @param lines lines to add
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public Builder optional(@NotNull String key, @NotNull String... lines) {
|
||||||
|
optional.put(key, Arrays.asList(lines));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add optional lines to the contents
|
||||||
|
*
|
||||||
|
* @param key key of the optional lines
|
||||||
|
* @param lines lines to add
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public Builder optional(@NotNull String key, @NotNull Iterable<String> lines) {
|
||||||
|
List<String> list = new ArrayList<>();
|
||||||
|
lines.forEach(list::add);
|
||||||
|
optional.put(key, list);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The built TextContents
|
||||||
|
*/
|
||||||
|
public TextContents build() {
|
||||||
|
return of(lines, optional);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+299
@@ -0,0 +1,299 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text.function;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.value.text.data.TextContents;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
public abstract class ContentHandler<RECEIVER, SELF extends ContentHandler<RECEIVER, SELF>> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to match the message insertion.
|
||||||
|
* <p>
|
||||||
|
* format:
|
||||||
|
* <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> original lines will not be parsed
|
||||||
|
* <br> example:
|
||||||
|
* <ul>
|
||||||
|
* <li>{- }#content-id#{1,1}</li>
|
||||||
|
* <li>@content-id@{1,1}</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public static final @NotNull Pattern INSERT_PATTERN = Pattern.compile(
|
||||||
|
"^(?:\\{(?<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 + ")";
|
||||||
|
|
||||||
|
protected BiFunction<RECEIVER, String, String> parser = (receiver, value) -> value;
|
||||||
|
protected String lineSeparator = System.lineSeparator();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to store the placeholders of the message
|
||||||
|
*/
|
||||||
|
protected @NotNull Map<String, Object> placeholders = new HashMap<>();
|
||||||
|
protected @NotNull UnaryOperator<String> paramFormatter = DEFAULT_PARAM_BUILDER;
|
||||||
|
protected @NotNull String[] params;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to store the insertion of the message
|
||||||
|
*/
|
||||||
|
protected @NotNull Map<String, @Nullable Function<RECEIVER, List<String>>> insertion = new HashMap<>();
|
||||||
|
protected boolean disableInsertion = false;
|
||||||
|
|
||||||
|
public abstract SELF self();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable the insertion of the text.
|
||||||
|
* <br> If the insertion is disabled, the text will be parsed directly.
|
||||||
|
*
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF disableInsertion() {
|
||||||
|
this.disableInsertion = true;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the insertion of the text.
|
||||||
|
*
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF enableInsertion() {
|
||||||
|
this.disableInsertion = false;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the line separator for the text.
|
||||||
|
*
|
||||||
|
* @param lineSeparator the line separator, default is {@link System#lineSeparator()}
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF lineSeparator(@NotNull String lineSeparator) {
|
||||||
|
this.lineSeparator = lineSeparator;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set all the placeholders for the text.
|
||||||
|
* <br> Will override the previous placeholders modifications.
|
||||||
|
*
|
||||||
|
* @param placeholders the placeholders
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF placeholders(@NotNull Map<String, Object> placeholders) {
|
||||||
|
this.placeholders = placeholders;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the placeholders for the text.
|
||||||
|
*
|
||||||
|
* @param consumer the placeholders
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF placeholders(@NotNull Consumer<Map<String, Object>> consumer) {
|
||||||
|
consumer.accept(this.placeholders);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the placeholders for the text.
|
||||||
|
*
|
||||||
|
* @param values The values to replace the {@link #params(String...)}.
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF placeholders(@Nullable Object... values) {
|
||||||
|
return placeholders(map -> map.putAll(buildParams(this.paramFormatter, this.params, values)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the placeholder for the text.
|
||||||
|
*
|
||||||
|
* @param key the key of the placeholder
|
||||||
|
* @param value the value of the placeholder
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF placeholder(@NotNull String key, @Nullable Object value) {
|
||||||
|
this.placeholders.put(paramFormatter.apply(key), value);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the params for the text,
|
||||||
|
* used for {@link #placeholders(Object...)} to build the placeholders.
|
||||||
|
*
|
||||||
|
* @param params the params
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF params(@NotNull String... params) {
|
||||||
|
this.params = params;
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @param id the id of the insertion text
|
||||||
|
* @param linesSupplier to supply the lines to insert
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF insert(@NotNull String id, @NotNull Function<RECEIVER, List<String>> linesSupplier) {
|
||||||
|
this.insertion.put(id, linesSupplier);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the specific contents by the id.
|
||||||
|
*
|
||||||
|
* @param id the id of the insertion text
|
||||||
|
* @param lines the lines to insert
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF insert(@NotNull String id, @NotNull String... lines) {
|
||||||
|
return insert(id, Arrays.asList(lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert the specific contents by the id.
|
||||||
|
*
|
||||||
|
* @param id the id of the insertion text
|
||||||
|
* @param lines the lines to insert
|
||||||
|
* @return the current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF insert(@NotNull String id, @NotNull List<String> lines) {
|
||||||
|
return insert(id, receiver -> lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the parser for the text.
|
||||||
|
*
|
||||||
|
* @param parser The parser
|
||||||
|
* @return The current {@link ContentHandler} instance
|
||||||
|
*/
|
||||||
|
public SELF parser(@NotNull BiFunction<RECEIVER, String, String> parser) {
|
||||||
|
this.parser = parser;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the supplied single text for the receiver.
|
||||||
|
*
|
||||||
|
* @param receiver the receiver
|
||||||
|
* @param text the text to parse
|
||||||
|
* @return the parsed text
|
||||||
|
*/
|
||||||
|
protected @Nullable String parse(@Nullable RECEIVER receiver, @NotNull String text) {
|
||||||
|
return this.parser.apply(receiver, setPlaceholders(text, this.placeholders));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(@NotNull TextContents contents, @Nullable RECEIVER receiver,
|
||||||
|
@NotNull Consumer<String> lineConsumer) {
|
||||||
|
if (contents.isEmpty()) return; // Nothing to parse
|
||||||
|
|
||||||
|
if (this.disableInsertion) {
|
||||||
|
contents.lines().forEach(line -> lineConsumer.accept(parse(receiver, line)));
|
||||||
|
return; // Simple parsed
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String line : contents.lines()) {
|
||||||
|
Matcher insertMatcher = INSERT_PATTERN.matcher(line);
|
||||||
|
if (insertMatcher.matches()) {
|
||||||
|
doInsert(insertMatcher, receiver, lineConsumer);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher enableMatcher = ENABLE_PATTERN.matcher(line);
|
||||||
|
if (enableMatcher.matches()) {
|
||||||
|
if (this.insertion.containsKey(enableMatcher.group("id"))) {
|
||||||
|
lineConsumer.accept(parse(receiver, enableMatcher.group("content")));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
@NotNull Map<String, Object> placeholders) {
|
||||||
|
if (messages.isEmpty()) return messages;
|
||||||
|
String parsed = messages;
|
||||||
|
for (Map.Entry<String, Object> entry : placeholders.entrySet()) {
|
||||||
|
parsed = parsed.replace(entry.getKey(), entry.getValue() == null ? "" : entry.getValue().toString());
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Object> buildParams(@NotNull UnaryOperator<String> paramBuilder,
|
||||||
|
@Nullable String[] params, @Nullable Object[] values) {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
if (params == null || params.length == 0) return map;
|
||||||
|
for (int i = 0; i < params.length; i++) {
|
||||||
|
map.put(paramBuilder.apply(params[i]), (values != null && values.length > i) ? values[i] : "?");
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text.function;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
public abstract class TextCompiler<MSG, RECEIVER, SELF extends TextCompiler<MSG, RECEIVER, SELF>> extends TextParser<RECEIVER, SELF> {
|
||||||
|
|
||||||
|
protected BiFunction<RECEIVER, String, MSG> compiler = (receiver, value) -> null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the text compiler.
|
||||||
|
*
|
||||||
|
* @param compiler The text compiler.
|
||||||
|
* @return The current {@link TextCompiler} instance.
|
||||||
|
*/
|
||||||
|
public SELF compiler(@NotNull BiFunction<RECEIVER, String, MSG> compiler) {
|
||||||
|
this.compiler = compiler;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile the text for specific receiver.
|
||||||
|
*
|
||||||
|
* @param receiver The receiver.
|
||||||
|
* @return The compiled text.
|
||||||
|
* @see #parse(Object, BiFunction)
|
||||||
|
*/
|
||||||
|
public @NotNull List<MSG> compile(@Nullable RECEIVER receiver) {
|
||||||
|
return parse(receiver, this.compiler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compile the singleton text for specific receiver.
|
||||||
|
*
|
||||||
|
* @param receiver The receiver.
|
||||||
|
* @return The compiled text.
|
||||||
|
* @see #parseLine(Object, BiFunction)
|
||||||
|
*/
|
||||||
|
public @Nullable MSG compileLine(@Nullable RECEIVER receiver) {
|
||||||
|
return parseLine(receiver, this.compiler);
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text.function;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.value.text.data.TextContents;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public abstract class TextDispatcher<MSG, RECEIVER, SELF extends TextDispatcher<MSG, RECEIVER, SELF>> extends TextCompiler<MSG, RECEIVER, SELF> {
|
||||||
|
|
||||||
|
protected @NotNull BiConsumer<RECEIVER, List<MSG>> dispatcher = (receiver, msg) -> {
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the dispatcher to send the message to the receiver
|
||||||
|
*
|
||||||
|
* @param dispatcher the dispatcher
|
||||||
|
* @return {@link TextDispatcher}
|
||||||
|
*/
|
||||||
|
public SELF dispatcher(@NotNull BiConsumer<RECEIVER, List<MSG>> dispatcher) {
|
||||||
|
this.dispatcher = dispatcher;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch the message to the receiver
|
||||||
|
*
|
||||||
|
* @param receivers the receivers
|
||||||
|
*/
|
||||||
|
@SafeVarargs
|
||||||
|
public final void to(@NotNull RECEIVER... receivers) {
|
||||||
|
if (receivers.length == 0) return;
|
||||||
|
to(Arrays.asList(receivers));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch the message to the receiver
|
||||||
|
*
|
||||||
|
* @param receivers the receivers
|
||||||
|
*/
|
||||||
|
public void to(@NotNull Iterable<? extends RECEIVER> receivers) {
|
||||||
|
for (RECEIVER receiver : receivers) {
|
||||||
|
List<MSG> msg = compile(receiver);
|
||||||
|
if (msg.isEmpty()) return;
|
||||||
|
dispatcher.accept(receiver, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatch the message to the receiver
|
||||||
|
*
|
||||||
|
* @param receivers the receivers
|
||||||
|
*/
|
||||||
|
public void to(@NotNull Supplier<Iterable<? extends RECEIVER>> receivers) {
|
||||||
|
to(receivers.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text.function;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.value.text.data.TextContents;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public abstract class TextParser<RECEIVER, SELF extends TextParser<RECEIVER, SELF>>
|
||||||
|
extends ContentHandler<RECEIVER, SELF> {
|
||||||
|
|
||||||
|
public abstract TextContents texts();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the texts for the receiver.
|
||||||
|
*
|
||||||
|
* @param receiver the receiver
|
||||||
|
* @return the parsed line
|
||||||
|
*/
|
||||||
|
public List<String> parse(@Nullable RECEIVER receiver) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
handle(receiver, result::add);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the texts for the receiver.
|
||||||
|
*
|
||||||
|
* @param receiver the receiver
|
||||||
|
* @param compiler the compiler
|
||||||
|
* @param <V> the type of the message
|
||||||
|
* @return the parsed line
|
||||||
|
*/
|
||||||
|
public <V> List<V> parse(@Nullable RECEIVER receiver, @NotNull BiFunction<RECEIVER, String, V> compiler) {
|
||||||
|
List<V> result = new ArrayList<>();
|
||||||
|
handle(receiver, s -> result.add(compiler.apply(receiver, s)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the texts as a single line for the receiver.
|
||||||
|
*
|
||||||
|
* @param receiver the receiver
|
||||||
|
* @return the parsed line
|
||||||
|
*/
|
||||||
|
public @Nullable String parseLine(@Nullable RECEIVER receiver) {
|
||||||
|
if (texts().isEmpty()) return null;
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
handle(receiver, s -> builder.append(s).append(this.lineSeparator));
|
||||||
|
// Remove the last line separator, if it exists
|
||||||
|
if (builder.length() > 0) builder.delete(builder.length() - this.lineSeparator.length(), builder.length());
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the texts as a single line for the receiver.
|
||||||
|
*
|
||||||
|
* @param receiver the receiver
|
||||||
|
* @param <V> the type of the message
|
||||||
|
* @return the parsed line
|
||||||
|
*/
|
||||||
|
public <V> @Nullable V parseLine(@Nullable RECEIVER receiver, @NotNull BiFunction<RECEIVER, String, V> compiler) {
|
||||||
|
return Optional.ofNullable(parseLine(receiver)).map(s -> compiler.apply(receiver, s)).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(@Nullable RECEIVER receiver, @NotNull Consumer<String> lineConsumer) {
|
||||||
|
handle(texts(), receiver, lineConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text.tests;
|
||||||
|
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
|
||||||
|
import cc.carm.lib.configuration.value.text.tests.conf.AppMessages;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class ConfigTest {
|
||||||
|
|
||||||
|
|
||||||
|
public static final String[] WEBSITES = new String[]{
|
||||||
|
"https://carm.cc",
|
||||||
|
"https://www.baidu.com",
|
||||||
|
"https://www.google.com"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
|
||||||
|
ConfigurationHolder<?> holder = JSONConfigFactory.from(new File("target/messages.json")).build();
|
||||||
|
|
||||||
|
holder.initialize(AppMessages.class);
|
||||||
|
|
||||||
|
System.out.println("--------------------------");
|
||||||
|
|
||||||
|
AppMessages.WELCOME.prepare()
|
||||||
|
.placeholders("Carm")
|
||||||
|
.insert("guidance")
|
||||||
|
.insert("websites", WEBSITES)
|
||||||
|
.to(System.out);
|
||||||
|
|
||||||
|
System.out.println("--------------------------");
|
||||||
|
|
||||||
|
AppMessages.NOT_AVAILABLE.sendTo(System.out);
|
||||||
|
|
||||||
|
System.out.println("--------------------------");
|
||||||
|
|
||||||
|
AppMessages.NO_PERMISSION.sendTo(System.out);
|
||||||
|
|
||||||
|
System.out.println("--------------------------");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text.tests;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.value.text.PreparedText;
|
||||||
|
import cc.carm.lib.configuration.value.text.data.TextContents;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class ParseTest {
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
|
||||||
|
List<String> lines = new ArrayList<>();
|
||||||
|
lines.add("Hello, %(name)");
|
||||||
|
lines.add("#more-creating#{1}");
|
||||||
|
lines.add("This is a test message");
|
||||||
|
lines.add("#guidance#");
|
||||||
|
lines.add("{- }#websites#{0,1}");
|
||||||
|
lines.add("Thanks for your reading!");
|
||||||
|
lines.add("?[click]");
|
||||||
|
lines.add("?[click]Click to see more!");
|
||||||
|
|
||||||
|
Map<String, List<String>> optional = new HashMap<>();
|
||||||
|
optional.put("guidance", Arrays.asList("To get more information for %(name), see:"));
|
||||||
|
optional.put("websites", Arrays.asList("https://www.baidu.com", "https://www.google.com"));
|
||||||
|
|
||||||
|
TextContents textContents = new TextContents(lines, optional);
|
||||||
|
|
||||||
|
PreparedText<String, PrintStream> msg = new PreparedText<String, PrintStream>(textContents)
|
||||||
|
.dispatcher((p, s) -> s.forEach(p::println))
|
||||||
|
.parser((p, s) -> s)
|
||||||
|
.compiler((p, s) -> s);
|
||||||
|
|
||||||
|
msg.placeholder("name", "Carm")
|
||||||
|
.insert("guidance")
|
||||||
|
.insert("click")
|
||||||
|
.insert("websites", "Baidu", "Bilibili", "Google");
|
||||||
|
|
||||||
|
System.out.println("----------------------------");
|
||||||
|
msg.to(System.out);
|
||||||
|
System.out.println("----------------------------");
|
||||||
|
System.out.println(msg.compileLine(System.out));
|
||||||
|
System.out.println("----------------------------");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text.tests.conf;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.Configuration;
|
||||||
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
|
|
||||||
|
@ConfigPath(root = true)
|
||||||
|
public interface AppMessages extends Configuration {
|
||||||
|
|
||||||
|
ConfiguredMsg WELCOME = ConfiguredMsg.builder()
|
||||||
|
.defaults(
|
||||||
|
"Hello, %(name)",
|
||||||
|
"#more-creating#{1}",
|
||||||
|
"This is a test message",
|
||||||
|
"#guidance#",
|
||||||
|
"{- }#websites#{0,1}",
|
||||||
|
"Thanks for your reading!")
|
||||||
|
.optional("guidance", "To get more information for %(name), see:")
|
||||||
|
.params("name").build();
|
||||||
|
|
||||||
|
ConfiguredMsg NO_PERMISSION = ConfiguredMsg.builder()
|
||||||
|
.defaults("Sorry! But you don't have permissions to do this.")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
ConfiguredMsg NOT_AVAILABLE = ConfiguredMsg.builder()
|
||||||
|
.defaults("Error! Service is not available now.", "Please contact your system manager.")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
}
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
package cc.carm.lib.configuration.value.text.tests.conf;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
|
import cc.carm.lib.configuration.value.text.ConfiguredText;
|
||||||
|
import cc.carm.lib.configuration.value.text.data.TextContents;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.PrintStream;
|
||||||
|
|
||||||
|
public class ConfiguredMsg extends ConfiguredText<String, PrintStream> {
|
||||||
|
|
||||||
|
public static @NotNull MsgBuilder builder() {
|
||||||
|
return new MsgBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @NotNull ConfiguredMsg of(@NotNull String... text) {
|
||||||
|
return builder().defaults(text).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfiguredMsg(@NotNull ValueManifest<TextContents> manifest, @NotNull String[] params) {
|
||||||
|
super(
|
||||||
|
manifest,
|
||||||
|
(p, s) -> s,
|
||||||
|
(p, s) -> s,
|
||||||
|
(p, s) -> s.forEach(p::println),
|
||||||
|
params
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void print(@NotNull Object... values) {
|
||||||
|
sendTo(System.out, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MsgBuilder extends Builder<String, PrintStream, MsgBuilder> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull MsgBuilder self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfiguredMsg build() {
|
||||||
|
return new ConfiguredMsg(buildManifest(), params);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.0</version>
|
<version>4.0.11</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
+1
-2
@@ -1,6 +1,5 @@
|
|||||||
package cc.carm.lib.configuration.annotation;
|
package cc.carm.lib.configuration.annotation;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Range;
|
import org.jetbrains.annotations.Range;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
@@ -22,7 +21,7 @@ public @interface ConfigVersion {
|
|||||||
*
|
*
|
||||||
* @return the version of the configuration field
|
* @return the version of the configuration field
|
||||||
*/
|
*/
|
||||||
@Range(from = 0, to = Integer.MAX_VALUE)
|
@Range(from = 0, to = 65535)
|
||||||
int value() default 0;
|
int value() default 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-13
@@ -1,13 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.option;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
|
||||||
|
|
||||||
public interface VersionedOptions {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to set newer defaults when a {@link cc.carm.lib.configuration.value.ConfigValue}'s marked version
|
|
||||||
* is newer than the current in storage.
|
|
||||||
*/
|
|
||||||
ConfigurationOption<Boolean> RESET_NEWER_DEFAULTS = ConfigurationOption.of(true);
|
|
||||||
|
|
||||||
}
|
|
||||||
+13
-2
@@ -1,4 +1,4 @@
|
|||||||
package cc.carm.lib.configuration.commentable;
|
package cc.carm.lib.configuration.versioned;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.annotation.ConfigVersion;
|
import cc.carm.lib.configuration.annotation.ConfigVersion;
|
||||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
@@ -19,7 +19,18 @@ public interface VersionedMetaTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void register(@NotNull ConfigurationInitializer initializer) {
|
static void register(@NotNull ConfigurationInitializer initializer) {
|
||||||
initializer.registerFieldAnnotation(ConfigVersion.class, VERSION, ConfigVersion::value);
|
initializer.appendFieldInitializer((holder, path, field, value) -> {
|
||||||
|
ConfigVersion annotation = field.getAnnotation(ConfigVersion.class);
|
||||||
|
if (annotation == null || value == null) return;
|
||||||
|
int currentVersion = annotation.value();
|
||||||
|
int savedVersion = holder.metadata(path).get(VERSION, 0);
|
||||||
|
if (currentVersion == savedVersion) return;
|
||||||
|
|
||||||
|
if (currentVersion > savedVersion) { // This values updated.
|
||||||
|
value.setDefault(true); // Mark as default value, force write.
|
||||||
|
}
|
||||||
|
holder.metadata(path).set(VERSION, currentVersion);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -15,25 +15,26 @@
|
|||||||
<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.0</version>
|
<version>4.0.11</version>
|
||||||
<modules>
|
<modules>
|
||||||
<module>core</module>
|
<module>core</module>
|
||||||
<module>features/section</module>
|
<module>features/section</module>
|
||||||
<module>features/file</module>
|
<module>features/file</module>
|
||||||
<module>features/commentable</module>
|
<module>features/commentable</module>
|
||||||
<module>features/versioned</module>
|
<module>features/versioned</module>
|
||||||
|
<module>features/text</module>
|
||||||
|
|
||||||
<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>
|
||||||
|
|
||||||
<module>demo</module>
|
<module>demo</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
<name>EasyConfiguration</name>
|
<name>EasyConfiguration</name>
|
||||||
<description>轻松(做)配置,简单便捷的通用配置文件加载、读取与更新工具,可自定义配置格式。</description>
|
<description>A simple, easy-to-use and universal solution for managing configuration files.</description>
|
||||||
<url>https://github.com/CarmJos/EasyConfiguration</url>
|
<url>https://github.com/CarmJos/EasyConfiguration</url>
|
||||||
|
|
||||||
<developers>
|
<developers>
|
||||||
@@ -88,12 +89,6 @@
|
|||||||
<url>https://repo1.maven.org/maven2/</url>
|
<url>https://repo1.maven.org/maven2/</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
<repository>
|
|
||||||
<id>github</id>
|
|
||||||
<name>GitHub Packages</name>
|
|
||||||
<url>https://maven.pkg.github.com/CarmJos/*</url>
|
|
||||||
</repository>
|
|
||||||
|
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
@@ -201,7 +196,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.13.0</version>
|
<version>3.14.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>${project.jdk.version}</source>
|
<source>${project.jdk.version}</source>
|
||||||
<target>${project.jdk.version}</target>
|
<target>${project.jdk.version}</target>
|
||||||
|
|||||||
@@ -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.0</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,7 +2,7 @@ package cc.carm.lib.configuration.source.json;
|
|||||||
|
|
||||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
import cc.carm.lib.configuration.source.file.FileConfigSource;
|
import cc.carm.lib.configuration.source.file.FileConfigSource;
|
||||||
import cc.carm.lib.configuration.source.section.MemorySection;
|
import cc.carm.lib.configuration.source.section.SourcedSection;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonSerializer;
|
import com.google.gson.JsonSerializer;
|
||||||
@@ -14,17 +14,17 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public class JSONSource extends FileConfigSource<MemorySection, Map<String, Object>, JSONSource> {
|
public class JSONSource extends FileConfigSource<SourcedSection, Map<String, Object>, JSONSource> {
|
||||||
|
|
||||||
public static final @NotNull Gson DEFAULT_GSON = new GsonBuilder()
|
public static final @NotNull Gson DEFAULT_GSON = new GsonBuilder()
|
||||||
.serializeNulls().disableHtmlEscaping().setPrettyPrinting()
|
.serializeNulls().disableHtmlEscaping().setPrettyPrinting()
|
||||||
.registerTypeAdapter(
|
.registerTypeAdapter(
|
||||||
MemorySection.class,
|
SourcedSection.class,
|
||||||
(JsonSerializer<MemorySection>) (src, t, c) -> c.serialize(src.data())
|
(JsonSerializer<SourcedSection>) (src, t, c) -> c.serialize(src.data())
|
||||||
).create();
|
).create();
|
||||||
|
|
||||||
protected final @NotNull Gson gson;
|
protected final @NotNull Gson gson;
|
||||||
protected @Nullable MemorySection rootSection;
|
protected @Nullable SourcedSection rootSection;
|
||||||
|
|
||||||
protected JSONSource(@NotNull ConfigurationHolder<? extends JSONSource> holder,
|
protected JSONSource(@NotNull ConfigurationHolder<? extends JSONSource> holder,
|
||||||
@NotNull File file, @Nullable String resourcePath) {
|
@NotNull File file, @Nullable String resourcePath) {
|
||||||
@@ -48,7 +48,7 @@ public class JSONSource extends FileConfigSource<MemorySection, Map<String, Obje
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected JSONSource self() {
|
protected @NotNull JSONSource self() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ public class JSONSource extends FileConfigSource<MemorySection, Map<String, Obje
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull MemorySection section() {
|
public @NotNull SourcedSection section() {
|
||||||
return Objects.requireNonNull(this.rootSection, "Root section is not initialized");
|
return Objects.requireNonNull(this.rootSection, "Root section is not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ public class JSONSource extends FileConfigSource<MemorySection, Map<String, Obje
|
|||||||
@Override
|
@Override
|
||||||
protected void onReload() throws Exception {
|
protected void onReload() throws Exception {
|
||||||
Map<?, ?> data = fileReader(reader -> gson.fromJson(reader, LinkedHashMap.class));
|
Map<?, ?> data = fileReader(reader -> gson.fromJson(reader, LinkedHashMap.class));
|
||||||
this.rootSection = MemorySection.root(this, data);
|
this.rootSection = SourcedSection.root(this, data);
|
||||||
this.lastUpdateMillis = System.currentTimeMillis(); // 更新时间
|
this.lastUpdateMillis = System.currentTimeMillis(); // 更新时间
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+48
@@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
-69
@@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+74
-14
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-18
@@ -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
|
||||||
+18
-10
@@ -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.0</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>
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
<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.mongodb.version>5.3.1</deps.mongodb.version>
|
||||||
<log4j.version>2.24.3</log4j.version>
|
<log4j.version>2.24.3</log4j.version>
|
||||||
</properties>
|
</properties>
|
||||||
<artifactId>easyconfiguration-mongodb</artifactId>
|
<artifactId>easyconfiguration-mongodb</artifactId>
|
||||||
@@ -30,7 +31,7 @@
|
|||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.parent.groupId}</groupId>
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
<artifactId>easyconfiguration-gson</artifactId>
|
<artifactId>easyconfiguration-feature-section</artifactId>
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -38,14 +39,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mongodb</groupId>
|
<groupId>org.mongodb</groupId>
|
||||||
<artifactId>mongodb-driver-sync</artifactId>
|
<artifactId>mongodb-driver-sync</artifactId>
|
||||||
<version>5.3.1</version>
|
<version>${deps.mongodb.version}</version>
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>${project.parent.groupId}</groupId>
|
|
||||||
<artifactId>easyconfiguration-demo</artifactId>
|
|
||||||
<version>${project.parent.version}</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -69,6 +63,20 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-demo</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-gson</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
package cc.carm.lib.configuration.source.mongodb;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationFactory;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class MongoConfigFactory extends ConfigurationFactory<MongoSource, ConfigurationHolder<MongoSource>, MongoConfigFactory> {
|
||||||
|
|
||||||
|
public static MongoConfigFactory from(@NotNull Supplier<MongoCollection<Document>> collectionSupplier) {
|
||||||
|
return new MongoConfigFactory(collectionSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MongoConfigFactory from(@NotNull MongoCollection<Document> collection) {
|
||||||
|
return from(() -> collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MongoConfigFactory from(@NotNull MongoDatabase database, @NotNull String collectionName) {
|
||||||
|
return from(() -> database.getCollection(collectionName));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected @NotNull Supplier<MongoCollection<Document>> collectionSupplier;
|
||||||
|
protected @NotNull String namespace = "config";
|
||||||
|
|
||||||
|
public MongoConfigFactory(@NotNull Supplier<MongoCollection<Document>> collectionSupplier) {
|
||||||
|
super();
|
||||||
|
this.collectionSupplier = collectionSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MongoConfigFactory collection(@NotNull Supplier<MongoCollection<Document>> collectionSupplier) {
|
||||||
|
this.collectionSupplier = collectionSupplier;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MongoConfigFactory collection(@NotNull MongoCollection<Document> collection) {
|
||||||
|
return collection(() -> collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MongoConfigFactory namespace(@NotNull String namespace) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MongoConfigFactory namespace(@NotNull Supplier<String> namespace) {
|
||||||
|
return namespace(namespace.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected MongoConfigFactory self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfigurationHolder<MongoSource> build() {
|
||||||
|
MongoCollection<Document> collection = this.collectionSupplier.get();
|
||||||
|
if (collection == null) {
|
||||||
|
throw new IllegalStateException("Failed to get MongoCollection<Document> from supplier");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ConfigurationHolder<MongoSource>(this.adapters, this.options, this.metadata, this.initializer) {
|
||||||
|
final @NotNull MongoSource source = new MongoSource(this, System.currentTimeMillis(), collection, namespace);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull MongoSource config() {
|
||||||
|
return this.source;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+73
-1
@@ -1,4 +1,76 @@
|
|||||||
package cc.carm.lib.configuration.source.mongodb;
|
package cc.carm.lib.configuration.source.mongodb;
|
||||||
|
|
||||||
public class MongoSource {
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||||
|
import cc.carm.lib.configuration.source.section.SourcedSection;
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import com.mongodb.client.model.ReplaceOptions;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class MongoSource extends ConfigureSource<SourcedSection, Map<String, Object>, MongoSource> {
|
||||||
|
|
||||||
|
protected final @NotNull MongoCollection<Document> collection;
|
||||||
|
protected final @NotNull String namespace;
|
||||||
|
|
||||||
|
protected SourcedSection rootSection;
|
||||||
|
|
||||||
|
protected MongoSource(@NotNull ConfigurationHolder<? extends MongoSource> holder, long lastUpdateMillis,
|
||||||
|
@NotNull MongoCollection<Document> collection, @NotNull String namespace) {
|
||||||
|
super(holder, lastUpdateMillis);
|
||||||
|
this.collection = collection;
|
||||||
|
this.namespace = namespace;
|
||||||
|
try {
|
||||||
|
onReload();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected @NotNull MongoSource self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Map<String, Object> original() {
|
||||||
|
return section().data();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull SourcedSection section() {
|
||||||
|
return Objects.requireNonNull(rootSection, "RootSection is not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String namespace() {
|
||||||
|
return this.namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull MongoCollection<Document> collection() {
|
||||||
|
return this.collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() throws Exception {
|
||||||
|
Map<String, Object> data = this.rootSection.asMap();
|
||||||
|
if (data.isEmpty()) return; // Skip saving if empty
|
||||||
|
if (data.containsKey("_id") && data.size() == 1) return; // Skip saving if only contains _id
|
||||||
|
|
||||||
|
ReplaceOptions options = new ReplaceOptions().upsert(true);
|
||||||
|
Document storage = new Document(data).append("_id", this.namespace);
|
||||||
|
this.collection.replaceOne(new Document("_id", this.namespace), storage, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onReload() throws Exception {
|
||||||
|
Document storage = this.collection.find(new Document("_id", this.namespace)).first();
|
||||||
|
if (storage == null) storage = new Document();
|
||||||
|
else storage.remove("_id"); // Remove _id
|
||||||
|
this.rootSection = SourcedSection.root(this, storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package config;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.Configuration;
|
||||||
|
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||||
|
|
||||||
|
interface MongoConfig extends Configuration {
|
||||||
|
ConfiguredValue<String> HOST = ConfiguredValue.of("127.0.0.1");
|
||||||
|
ConfiguredValue<Integer> PORT = ConfiguredValue.of(27017);
|
||||||
|
ConfiguredValue<String> USERNAME = ConfiguredValue.of("minecraft");
|
||||||
|
ConfiguredValue<String> PASSWORD = ConfiguredValue.of("minecraft");
|
||||||
|
ConfiguredValue<String> DATABASE = ConfiguredValue.of("minecraft");
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package config;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
|
||||||
|
import cc.carm.lib.configuration.source.mongodb.MongoConfigFactory;
|
||||||
|
import com.mongodb.ConnectionString;
|
||||||
|
import com.mongodb.MongoClientSettings;
|
||||||
|
import com.mongodb.MongoCredential;
|
||||||
|
import com.mongodb.client.MongoClient;
|
||||||
|
import com.mongodb.client.MongoClients;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import org.bson.UuidRepresentation;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class MongoTest {
|
||||||
|
|
||||||
|
boolean local = false;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test() {
|
||||||
|
|
||||||
|
if (!local) return;
|
||||||
|
|
||||||
|
ConfigurationHolder<?> gsonHolder = JSONConfigFactory.from(new File("target/mongo.json")).build();
|
||||||
|
gsonHolder.initialize(MongoConfig.class);
|
||||||
|
|
||||||
|
MongoClientSettings settings = MongoClientSettings.builder()
|
||||||
|
.applyConnectionString(new ConnectionString(
|
||||||
|
"mongodb://" + MongoConfig.HOST.resolve() + ":" + MongoConfig.PORT.resolve()
|
||||||
|
))
|
||||||
|
.credential(MongoCredential.createCredential(
|
||||||
|
MongoConfig.USERNAME.resolve(), MongoConfig.DATABASE.resolve(),
|
||||||
|
MongoConfig.PASSWORD.resolve().toCharArray()
|
||||||
|
))
|
||||||
|
.uuidRepresentation(UuidRepresentation.STANDARD)
|
||||||
|
.build();
|
||||||
|
MongoClient mongoClient = MongoClients.create(settings);
|
||||||
|
MongoDatabase mongoDatabase = mongoClient.getDatabase(MongoConfig.DATABASE.resolve());
|
||||||
|
|
||||||
|
ConfigurationHolder<?> mongoHolder = MongoConfigFactory
|
||||||
|
.from(mongoDatabase, "configs")
|
||||||
|
.namespace("my_plugin")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// Test the configuration
|
||||||
|
ConfigurationTest.testDemo(mongoHolder);
|
||||||
|
ConfigurationTest.testInner(mongoHolder);
|
||||||
|
|
||||||
|
ConfigurationTest.save(mongoHolder);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,10 +10,10 @@ CREATE TABLE IF NOT EXISTS conf
|
|||||||
`path` VARCHAR(96) NOT NULL, # 配置路径 (ConfigPath)
|
`path` VARCHAR(96) NOT NULL, # 配置路径 (ConfigPath)
|
||||||
`type` TINYINT UNSIGNED NOT NULL DEFAULT 0, # 数据类型 (Integer/Byte/List/Map/...)
|
`type` TINYINT UNSIGNED NOT NULL DEFAULT 0, # 数据类型 (Integer/Byte/List/Map/...)
|
||||||
`value` MEDIUMTEXT, # 配置项的值 (可能为JSON格式)
|
`value` MEDIUMTEXT, # 配置项的值 (可能为JSON格式)
|
||||||
`inline_comment` TEXT, # 配置项的用法,本质是行内注释
|
`inline_comment` TEXT comment 'usage', # 配置项的用法,本质是行内注释
|
||||||
`header_comment` MEDIUMTEXT, # 配置项的描述,本质是顶部注释
|
`header_comment` MEDIUMTEXT comment 'description', # 配置项的描述,本质是顶部注释
|
||||||
`footer_comment` MEDIUMTEXT, # 配置项的描述,本质是顶部注释
|
`footer_comment` MEDIUMTEXT comment 'example', # 配置项的参考,本质是底部注释
|
||||||
`version` INT UNSIGNED NOT NULL DEFAULT 0, # 配置项的版本
|
`version` MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, # 配置项的版本
|
||||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, # 创建时间
|
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, # 创建时间
|
||||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
PRIMARY KEY (`namespace`, `path`)
|
PRIMARY KEY (`namespace`, `path`)
|
||||||
|
|||||||
+20
-5
@@ -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.0</version>
|
<version>4.0.11</version>
|
||||||
<relativePath>../../pom.xml</relativePath>
|
<relativePath>../../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<properties>
|
<properties>
|
||||||
@@ -15,7 +15,8 @@
|
|||||||
<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>
|
||||||
|
|
||||||
<log4j.version>2.24.3</log4j.version>
|
<deps.mysql-driver.version>8.0.33</deps.mysql-driver.version>
|
||||||
|
<deps.log4j.version>2.24.3</deps.log4j.version>
|
||||||
</properties>
|
</properties>
|
||||||
<artifactId>easyconfiguration-sql</artifactId>
|
<artifactId>easyconfiguration-sql</artifactId>
|
||||||
|
|
||||||
@@ -77,21 +78,35 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-api</artifactId>
|
<artifactId>log4j-api</artifactId>
|
||||||
<version>${log4j.version}</version>
|
<version>${deps.log4j.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-core</artifactId>
|
<artifactId>log4j-core</artifactId>
|
||||||
<version>${log4j.version}</version>
|
<version>${deps.log4j.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-slf4j-impl</artifactId>
|
<artifactId>log4j-slf4j-impl</artifactId>
|
||||||
<version>${log4j.version}</version>
|
<version>${deps.log4j.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>easyconfiguration-gson</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>mysql</groupId>
|
||||||
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
|
<version>${deps.mysql-driver.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
|||||||
+178
@@ -0,0 +1,178 @@
|
|||||||
|
package cc.carm.lib.configuration.source.sql;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.commentable.Commentable;
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationFactory;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.versioned.VersionedMetaTypes;
|
||||||
|
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.GsonBuilder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Range;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class SQLConfigFactory extends ConfigurationFactory<SQLSource, ConfigurationHolder<SQLSource>, SQLConfigFactory> {
|
||||||
|
|
||||||
|
public static SQLConfigFactory from(@NotNull Supplier<@NotNull SQLManager> managerSupplier) {
|
||||||
|
return new SQLConfigFactory(managerSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SQLConfigFactory from(@NotNull SQLManager 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 Supplier<Gson> gsonSupplier = () -> DEFAULT_GSON;
|
||||||
|
|
||||||
|
protected HashMap<Integer, SQLValueResolver<?>> resolvers = new HashMap<>(SQLValueResolver.STANDARD_RESOLVERS);
|
||||||
|
protected SQLTable tableName = SQLTable.of("config", (table) -> DEFAULT_TABLE_SCHEMA.accept("config", table));
|
||||||
|
protected String namespace = "default";
|
||||||
|
|
||||||
|
public SQLConfigFactory(@NotNull Supplier<SQLManager> managerSupplier) {
|
||||||
|
this.managerSupplier = managerSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SQLConfigFactory self() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLConfigFactory manager(@NotNull Supplier<SQLManager> managerSupplier) {
|
||||||
|
this.managerSupplier = managerSupplier;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLConfigFactory manager(@NotNull SQLManager manager) {
|
||||||
|
return manager(() -> manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLConfigFactory gson(@NotNull Supplier<Gson> gsonSupplier) {
|
||||||
|
this.gsonSupplier = gsonSupplier;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLConfigFactory gson(@NotNull Consumer<GsonBuilder> builder) {
|
||||||
|
return gson(() -> {
|
||||||
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
|
builder.accept(gsonBuilder);
|
||||||
|
return gsonBuilder.create();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLConfigFactory gson(@NotNull Gson gson) {
|
||||||
|
return gson(() -> gson);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLConfigFactory resolver(@Range(from = 0, to = 255) int type, @NotNull SQLValueResolver<?> resolver) {
|
||||||
|
this.resolvers.put(type, resolver);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SQLConfigFactory resolver(@Range(from = 0, to = 255) int id, @NotNull Class<T> clazz,
|
||||||
|
@NotNull DataFunction<String, T> parser) {
|
||||||
|
return resolver(id, ValueType.of(clazz), parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SQLConfigFactory resolver(@Range(from = 0, to = 255) int id, @NotNull ValueType<T> type,
|
||||||
|
@NotNull DataFunction<String, T> parser) {
|
||||||
|
return resolver(id, SQLValueResolver.of(type, parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SQLConfigFactory resolver(@Range(from = 0, to = 255) int id, @NotNull Class<T> clazz,
|
||||||
|
@NotNull DataFunction<String, T> parser,
|
||||||
|
@NotNull DataFunction<T, String> serializer) {
|
||||||
|
return resolver(id, ValueType.of(clazz), parser, serializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> SQLConfigFactory resolver(@Range(from = 0, to = 255) int id, @NotNull ValueType<T> type,
|
||||||
|
@NotNull DataFunction<String, T> parser,
|
||||||
|
@NotNull DataFunction<T, String> serializer) {
|
||||||
|
return resolver(id, SQLValueResolver.of(type, parser, serializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLConfigFactory table(@NotNull SQLTable table) {
|
||||||
|
this.tableName = table;
|
||||||
|
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) {
|
||||||
|
this.namespace = namespace;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfigurationHolder<SQLSource> build() {
|
||||||
|
Gson gson = gsonSupplier.get();
|
||||||
|
if (gson == null) throw new NullPointerException("No Gson instance provided.");
|
||||||
|
|
||||||
|
SQLManager manager = this.managerSupplier.get();
|
||||||
|
if (manager == null) throw new NullPointerException("No SQLManager instance provided.");
|
||||||
|
|
||||||
|
Commentable.registerMeta(this.initializer);
|
||||||
|
VersionedMetaTypes.register(this.initializer);
|
||||||
|
|
||||||
|
return new ConfigurationHolder<SQLSource>(this.adapters, this.options, this.metadata, this.initializer) {
|
||||||
|
final SQLSource source = new SQLSource(
|
||||||
|
this, System.currentTimeMillis(),
|
||||||
|
gson, manager, resolvers, tableName, namespace
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull SQLSource config() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
package cc.carm.lib.configuration.source.sql;
|
package cc.carm.lib.configuration.source.sql;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||||
|
|
||||||
public interface SQLOptions {
|
public interface SQLOptions {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to purge the configuration's in-database data when saving.
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Boolean> PURGE = ConfigurationOption.of( true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,136 +1,170 @@
|
|||||||
package cc.carm.lib.configuration.source.sql;
|
package cc.carm.lib.configuration.source.sql;
|
||||||
|
|
||||||
|
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.section.ConfigureSource;
|
||||||
import cc.carm.lib.configuration.source.section.MemorySection;
|
import cc.carm.lib.configuration.source.section.SourcedSection;
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
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.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 java.sql.ResultSet;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class SQLSource extends ConfigureSource<MemorySection, Map<?, ?>, SQLSource> {
|
public class SQLSource extends ConfigureSource<SourcedSection, Map<String, Object>, SQLSource> {
|
||||||
|
|
||||||
protected static final @NotNull Gson DEFAULT_GSON = new GsonBuilder()
|
|
||||||
.serializeNulls().disableHtmlEscaping().setPrettyPrinting()
|
|
||||||
.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;
|
||||||
protected final @NotNull SQLTable table;
|
protected final @NotNull SQLTable table;
|
||||||
|
|
||||||
protected final @NotNull Set<String> updated = new HashSet<>();
|
protected final @NotNull Map<Integer, SQLValueResolver<?>> resolvers;
|
||||||
protected MemorySection rootSection;
|
protected SourcedSection rootSection;
|
||||||
|
|
||||||
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 String tableName, @NotNull String namespace) {
|
@NotNull Gson gson, @NotNull SQLManager sqlManager,
|
||||||
|
@NotNull Map<Integer, SQLValueResolver<?>> resolvers,
|
||||||
|
@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.namespace = namespace;
|
this.namespace = namespace;
|
||||||
this.table = SQLTable.of(tableName, builder -> {
|
this.table = table;
|
||||||
|
|
||||||
builder.addColumn("namespace", "VARCHAR(32) NOT NULL");
|
try {
|
||||||
builder.addColumn("path", "VARCHAR(96) NOT NULL");
|
this.table.create(this.sqlManager);
|
||||||
|
onReload();
|
||||||
builder.addColumn("type", "TINYINT NOT NULL DEFAULT 0");
|
} catch (Exception e) {
|
||||||
builder.addColumn("value", "TEXT");
|
e.printStackTrace();
|
||||||
|
}
|
||||||
builder.addColumn("inline_comment", "TEXT");
|
|
||||||
builder.addColumn("header_comments", "MEDIUMTEXT");
|
|
||||||
builder.addColumn("footer_comments", "MEDIUMTEXT");
|
|
||||||
|
|
||||||
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");
|
|
||||||
|
|
||||||
builder.setIndex(
|
|
||||||
IndexType.PRIMARY_KEY, "pk_" + tableName.toLowerCase(),
|
|
||||||
"namespace", "path"
|
|
||||||
);
|
|
||||||
builder.setTableSettings("ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SQLSource self() {
|
protected @NotNull SQLSource self() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NotNull Gson gson() {
|
||||||
|
return gson;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull Map<?, ?> original() {
|
public @NotNull Map<String, Object> original() {
|
||||||
return section().data();
|
return section().data();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull MemorySection section() {
|
public @NotNull SourcedSection section() {
|
||||||
return Objects.requireNonNull(this.rootSection, "Root section is not initialized.");
|
return Objects.requireNonNull(this.rootSection, "Root section is not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int purge() throws Exception {
|
||||||
|
return this.table.createDelete().addCondition("namespace", namespace).build().execute();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save() throws Exception {
|
public void save() throws Exception {
|
||||||
if (this.updated.isEmpty()) return; // Nothing to save
|
LocalDateTime time = LocalDateTime.now(); // Update time
|
||||||
|
List<Object[]> dataValues = new ArrayList<>();
|
||||||
|
|
||||||
Date date = new Date(); // Update time
|
SourcedSection section = section();
|
||||||
List<Object[]> values = new ArrayList<>();
|
Map<String, ConfigValue<?>> values = holder().registeredValues();
|
||||||
|
for (Map.Entry<String, ConfigValue<?>> entry : values.entrySet()) {
|
||||||
|
@NotNull String path = entry.getKey();
|
||||||
|
@NotNull ConfigValue<?> config = entry.getValue();
|
||||||
|
@Nullable Object value = section.get(path);
|
||||||
|
|
||||||
for (String path : this.updated) {
|
if (value instanceof SourcedSection) {
|
||||||
Object value = get(path);
|
value = ((SourcedSection) value).asMap();
|
||||||
|
} else if (value instanceof List<?>) {
|
||||||
// if (value instanceof SQLConfigWrapper) {
|
List<Object> list = new ArrayList<>();
|
||||||
// value = getSourceMap(((SQLConfigWrapper) value).getSource());
|
for (Object obj : (List<?>) value) {
|
||||||
// }
|
if (obj instanceof SourcedSection) {
|
||||||
//
|
list.add(((SourcedSection) obj).asMap());
|
||||||
// SQLValueResolver<?> type = SQLValueTypes.get(value.getClass());
|
} else {
|
||||||
// if (type != null) {
|
list.add(obj);
|
||||||
// values.add(new Object[]{
|
}
|
||||||
// this.namespace, path, date,
|
}
|
||||||
// type.getID(), type.serializeObject(value),
|
value = list;
|
||||||
// getComments().getInlineComment(path),
|
|
||||||
// GSON.toJson(getComments().getHeaderComment(path))
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updated.clear();
|
if (value == null) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
int typeID = typeIdOf(value);
|
||||||
|
String data = serialize(typeID, value);
|
||||||
|
if (data == null) continue;
|
||||||
|
|
||||||
|
int version = holder().metadata(path).get(VersionedMetaTypes.VERSION, 0);
|
||||||
|
dataValues.add(new Object[]{
|
||||||
|
namespace, path, time, version, typeID, data,
|
||||||
|
Commentable.getInlineComment(holder(), path),
|
||||||
|
gson.toJson(Commentable.getHeaderComments(holder(), path)),
|
||||||
|
gson.toJson(Commentable.getFooterComments(holder(), path))
|
||||||
|
});
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holder.option(SQLOptions.PURGE)) {
|
||||||
|
purge();
|
||||||
|
}
|
||||||
this.table.createReplaceBatch()
|
this.table.createReplaceBatch()
|
||||||
.setColumnNames("namespace", "path", "update_time", "type", "value", "inline_comment", "header_comments")
|
.setColumnNames(
|
||||||
.setAllParams(values)
|
"namespace", "path", "update_time", "version", "type", "value",
|
||||||
.execute();
|
"inline_comment", "header_comments", "footer_comments"
|
||||||
|
).setAllParams(dataValues).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onReload() throws Exception {
|
protected void onReload() throws Exception {
|
||||||
LinkedHashMap<String, Object> values = new LinkedHashMap<>();
|
Map<String, Object> loaded = new LinkedHashMap<>();
|
||||||
|
try (SQLQuery query = this.table.createQuery()
|
||||||
// try (SQLQuery query = this.table.createQuery()
|
.addCondition("namespace", namespace)
|
||||||
// .addCondition("namespace", namespace)
|
.build().execute()) {
|
||||||
// .build().execute()) {
|
ResultSet rs = query.getResultSet();
|
||||||
// ResultSet rs = query.getResultSet();
|
while (rs.next()) {
|
||||||
// while (rs.next()) {
|
String path = rs.getString("path");
|
||||||
// String path = rs.getString("path");
|
if (path == null) continue; // Path should be not null
|
||||||
// int type = rs.getInt("type");
|
int ver = rs.getInt("version");
|
||||||
// try {
|
try {
|
||||||
// SQLValueResolver<?> resolver = SQLValueTypes.get(type);
|
loaded.put(path, parse(rs.getInt("type"), rs.getString("value")));
|
||||||
// if (resolver == null) throw new IllegalStateException("No resolver for type #" + type);
|
if (ver != 0) holder().metadata(path).set(VersionedMetaTypes.VERSION, ver);
|
||||||
// String value = rs.getString("value");
|
} catch (Exception e) {
|
||||||
// values.put(path, resolver.resolve(value));
|
e.printStackTrace();
|
||||||
//
|
}
|
||||||
// loadInlineComment(path, rs.getString("inline_comment"));
|
}
|
||||||
// loadHeaderComment(path, rs.getString("header_comments"));
|
}
|
||||||
// } catch (Exception ex) {
|
this.rootSection = SourcedSection.root(this, loaded);
|
||||||
// ex.printStackTrace();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.rootConfiguration = new SQLConfigWrapper(this, values);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected @Nullable Object parse(int type, String value) throws Exception {
|
||||||
|
SQLValueResolver<?> function = this.resolvers.get(type);
|
||||||
|
if (function == null) throw new IllegalStateException("No resolvers for type #" + type);
|
||||||
|
return function.resolve(this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected @Nullable String serialize(int type, @NotNull Object value) throws Exception {
|
||||||
|
SQLValueResolver<?> function = this.resolvers.get(type);
|
||||||
|
if (function == null) throw new IllegalStateException("No resolvers for type #" + type);
|
||||||
|
return function.serialize(this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int typeIdOf(@NotNull Object value) {
|
||||||
|
return this.resolvers.entrySet().stream()
|
||||||
|
.filter(entry -> entry.getValue().isInstance(value))
|
||||||
|
.findFirst().map(Map.Entry::getKey)
|
||||||
|
.orElseThrow(() -> new IllegalStateException("No resolvers for value " + value.getClass().getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
package cc.carm.lib.configuration.source.sql;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public abstract class SQLValueResolver<T> {
|
||||||
|
|
||||||
|
public static final @NotNull SQLValueResolver<String> STRING = SQLValueResolver.of(ValueType.STRING, s -> s);
|
||||||
|
public static final @NotNull SQLValueResolver<Byte> BYTE = SQLValueResolver.of(ValueType.BYTE, Byte::parseByte);
|
||||||
|
public static final @NotNull SQLValueResolver<Short> SHORT = SQLValueResolver.of(ValueType.SHORT, Short::parseShort);
|
||||||
|
public static final @NotNull SQLValueResolver<Integer> INTEGER = SQLValueResolver.of(ValueType.INTEGER, Integer::parseInt);
|
||||||
|
public static final @NotNull SQLValueResolver<Long> LONG = SQLValueResolver.of(ValueType.LONG, Long::parseLong);
|
||||||
|
public static final @NotNull SQLValueResolver<Float> FLOAT = SQLValueResolver.of(ValueType.FLOAT, Float::parseFloat);
|
||||||
|
public static final @NotNull SQLValueResolver<Double> DOUBLE = SQLValueResolver.of(ValueType.DOUBLE, Double::parseDouble);
|
||||||
|
public static final @NotNull SQLValueResolver<Boolean> BOOLEAN = SQLValueResolver.of(ValueType.BOOLEAN, Boolean::parseBoolean);
|
||||||
|
public static final @NotNull SQLValueResolver<Character> CHAR = SQLValueResolver.of(ValueType.CHAR, s -> s.charAt(0));
|
||||||
|
|
||||||
|
public static final @NotNull SQLValueResolver<List<?>> LIST = new SQLValueResolver<List<?>>(new ValueType<List<?>>() {
|
||||||
|
}) {
|
||||||
|
@Override
|
||||||
|
public @Nullable List<?> resolve(@NotNull SQLSource source, String data) throws Exception {
|
||||||
|
return source.gson().fromJson(data, List.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String serialize(@NotNull SQLSource source, Object value) {
|
||||||
|
return source.gson().toJson(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final @NotNull SQLValueResolver<Map<?, ?>> MAP = new SQLValueResolver<Map<?, ?>>(new ValueType<Map<?, ?>>() {
|
||||||
|
}) {
|
||||||
|
@Override
|
||||||
|
public @Nullable Map<?, ?> resolve(@NotNull SQLSource source, String data) throws Exception {
|
||||||
|
return source.gson().fromJson(data, LinkedHashMap.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable String serialize(@NotNull SQLSource source, Object value) {
|
||||||
|
return source.gson().toJson(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final @NotNull Map<Integer, SQLValueResolver<?>> STANDARD_RESOLVERS = Collections.unmodifiableMap(standards());
|
||||||
|
|
||||||
|
static Map<Integer, SQLValueResolver<?>> standards() {
|
||||||
|
Map<Integer, SQLValueResolver<?>> map = new LinkedHashMap<>();
|
||||||
|
map.put(0, STRING);
|
||||||
|
map.put(1, BYTE);
|
||||||
|
map.put(2, SHORT);
|
||||||
|
map.put(3, INTEGER);
|
||||||
|
map.put(4, LONG);
|
||||||
|
map.put(5, FLOAT);
|
||||||
|
map.put(6, DOUBLE);
|
||||||
|
map.put(7, BOOLEAN);
|
||||||
|
map.put(8, CHAR);
|
||||||
|
map.put(10, LIST);
|
||||||
|
map.put(11, MAP);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> SQLValueResolver<V> of(@NotNull ValueType<V> type, @NotNull DataFunction<String, V> resolver) {
|
||||||
|
return new SQLValueResolver<V>(type) {
|
||||||
|
@Override
|
||||||
|
public @NotNull V resolve(@NotNull SQLSource source, String data) throws Exception {
|
||||||
|
return resolver.handle(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <V> SQLValueResolver<V> of(@NotNull ValueType<V> type,
|
||||||
|
@NotNull DataFunction<String, V> resolver,
|
||||||
|
@NotNull DataFunction<V, String> serializer) {
|
||||||
|
return new SQLValueResolver<V>(type) {
|
||||||
|
@Override
|
||||||
|
public @NotNull V resolve(@NotNull SQLSource source, String data) throws Exception {
|
||||||
|
return resolver.handle(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull String serialize(@NotNull SQLSource source, Object value) throws Exception {
|
||||||
|
return serializer.handle(type.cast(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<T> type;
|
||||||
|
|
||||||
|
protected SQLValueResolver(@NotNull ValueType<T> type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueType<T> getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInstance(@NotNull Object obj) {
|
||||||
|
return getType().isInstance(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract @Nullable T resolve(@NotNull SQLSource source, String data) throws Exception;
|
||||||
|
|
||||||
|
public @Nullable String serialize(@NotNull SQLSource source, Object value) throws Exception {
|
||||||
|
return String.valueOf(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,27 +1,48 @@
|
|||||||
package config;
|
package config;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.demo.DatabaseConfiguration;
|
||||||
|
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
|
||||||
|
import cc.carm.lib.configuration.source.sql.SQLConfigFactory;
|
||||||
import cc.carm.lib.easysql.EasySQL;
|
import cc.carm.lib.easysql.EasySQL;
|
||||||
import cc.carm.lib.easysql.api.SQLManager;
|
import cc.carm.lib.easysql.api.SQLManager;
|
||||||
import cc.carm.lib.easysql.beecp.BeeDataSourceConfig;
|
import cc.carm.lib.easysql.beecp.BeeDataSourceConfig;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class SQLConfigTest {
|
public class SQLConfigTest {
|
||||||
|
|
||||||
|
boolean local = false;
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test() {
|
public void test() {
|
||||||
|
if (!local) return;
|
||||||
|
|
||||||
|
ConfigurationHolder<?> gsonHolder = JSONConfigFactory.from(new File("target/sql.json")).build();
|
||||||
|
gsonHolder.initialize(DatabaseConfiguration.class);
|
||||||
|
|
||||||
BeeDataSourceConfig config = new BeeDataSourceConfig();
|
BeeDataSourceConfig config = new BeeDataSourceConfig();
|
||||||
config.setDriverClassName("org.h2.Driver");
|
config.setDriverClassName(DatabaseConfiguration.DRIVER_NAME.resolve());
|
||||||
config.setJdbcUrl("jdbc:h2:file:target/test;DB_CLOSE_DELAY=-1;MODE=MySQL;");
|
config.setJdbcUrl(DatabaseConfiguration.buildJDBC());
|
||||||
|
config.setUsername(DatabaseConfiguration.USERNAME.resolve());
|
||||||
|
config.setPassword(DatabaseConfiguration.PASSWORD.resolve());
|
||||||
|
|
||||||
SQLManager manager = EasySQL.createManager(config);
|
SQLManager manager = EasySQL.createManager(config);
|
||||||
manager.setDebugMode(true);
|
manager.setDebugMode(true);
|
||||||
|
|
||||||
// SQLConfigProvider provider = EasyConfiguration.from(manager, "conf_test", "TESTING");
|
ConfigurationHolder<?> holder = SQLConfigFactory.from(manager)
|
||||||
//
|
.tableName("test_configs")
|
||||||
// ConfigurationTest.testDemo(provider);
|
.namespace("testing")
|
||||||
// ConfigurationTest.testInner(provider);
|
.build();
|
||||||
//
|
|
||||||
// ConfigurationTest.save(provider);
|
ConfigurationTest.testDemo(holder);
|
||||||
//
|
ConfigurationTest.testInner(holder);
|
||||||
// EasySQL.shutdownManager(manager);
|
|
||||||
|
ConfigurationTest.save(holder);
|
||||||
|
|
||||||
|
EasySQL.shutdownManager(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,74 +62,3 @@ dependencies {
|
|||||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
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
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -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.0</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.1.0</deps.yamlcommentwriter.version>
|
<deps.yamlcommentwriter.version>1.2.1</deps.yamlcommentwriter.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<artifactId>easyconfiguration-yaml</artifactId>
|
<artifactId>easyconfiguration-yaml</artifactId>
|
||||||
@@ -29,13 +29,6 @@
|
|||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>cc.carm.lib</groupId>
|
|
||||||
<artifactId>yamlcommentwriter</artifactId>
|
|
||||||
<version>1.1.0</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.parent.groupId}</groupId>
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
<artifactId>easyconfiguration-feature-file</artifactId>
|
<artifactId>easyconfiguration-feature-file</artifactId>
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
package cc.carm.lib.configuration.source.yaml;
|
package cc.carm.lib.configuration.source.yaml;
|
||||||
|
|
||||||
|
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.source.ConfigurationHolder;
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
import cc.carm.lib.configuration.source.file.FileConfigFactory;
|
import cc.carm.lib.configuration.source.file.FileConfigFactory;
|
||||||
@@ -125,7 +126,7 @@ public class YAMLConfigFactory extends FileConfigFactory<YAMLSource, Configurati
|
|||||||
File configFile = this.file;
|
File configFile = this.file;
|
||||||
String sourcePath = this.resourcePath;
|
String sourcePath = this.resourcePath;
|
||||||
|
|
||||||
CommentableMeta.register(this.initializer); // Register commentable meta types
|
Commentable.registerMeta(this.initializer); // Register commentable meta types
|
||||||
|
|
||||||
return new ConfigurationHolder<YAMLSource>(this.adapters, this.options, this.metadata, this.initializer) {
|
return new ConfigurationHolder<YAMLSource>(this.adapters, this.options, this.metadata, this.initializer) {
|
||||||
final @NotNull YAMLSource source = new YAMLSource(this, configFile, sourcePath);
|
final @NotNull YAMLSource source = new YAMLSource(this, configFile, sourcePath);
|
||||||
|
|||||||
+19
-53
@@ -1,12 +1,11 @@
|
|||||||
package cc.carm.lib.configuration.source.yaml;
|
package cc.carm.lib.configuration.source.yaml;
|
||||||
|
|
||||||
import cc.carm.lib.configuration.commentable.CommentableMeta;
|
import cc.carm.lib.configuration.commentable.Commentable;
|
||||||
import cc.carm.lib.configuration.option.CommentableOptions;
|
import cc.carm.lib.configuration.commentable.CommentableOptions;
|
||||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
import cc.carm.lib.configuration.source.file.FileConfigSource;
|
import cc.carm.lib.configuration.source.file.FileConfigSource;
|
||||||
import cc.carm.lib.configuration.source.option.StandardOptions;
|
|
||||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||||
import cc.carm.lib.configuration.source.section.MemorySection;
|
import cc.carm.lib.configuration.source.section.SourcedSection;
|
||||||
import cc.carm.lib.yamlcommentupdater.CommentedSection;
|
import cc.carm.lib.yamlcommentupdater.CommentedSection;
|
||||||
import cc.carm.lib.yamlcommentupdater.CommentedYAMLWriter;
|
import cc.carm.lib.yamlcommentupdater.CommentedYAMLWriter;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -25,15 +24,16 @@ import java.io.Reader;
|
|||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class YAMLSource extends FileConfigSource<MemorySection, Map<String, Object>, YAMLSource> implements CommentedSection {
|
public class YAMLSource
|
||||||
|
extends FileConfigSource<SourcedSection, Map<String, Object>, YAMLSource>
|
||||||
|
implements CommentedSection {
|
||||||
|
|
||||||
protected final @NotNull YamlConstructor yamlConstructor;
|
protected final @NotNull YamlConstructor yamlConstructor;
|
||||||
protected final @NotNull YamlRepresenter yamlRepresenter;
|
protected final @NotNull YamlRepresenter yamlRepresenter;
|
||||||
protected final @NotNull Yaml yaml;
|
protected final @NotNull Yaml yaml;
|
||||||
|
|
||||||
protected @Nullable MemorySection rootSection;
|
protected @Nullable SourcedSection rootSection;
|
||||||
|
|
||||||
protected YAMLSource(@NotNull ConfigurationHolder<? extends YAMLSource> holder,
|
protected YAMLSource(@NotNull ConfigurationHolder<? extends YAMLSource> holder,
|
||||||
@NotNull File file, @Nullable String resourcePath) {
|
@NotNull File file, @Nullable String resourcePath) {
|
||||||
@@ -55,8 +55,8 @@ public class YAMLSource extends FileConfigSource<MemorySection, Map<String, Obje
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected YAMLSource self() {
|
protected @NotNull YAMLSource self() {
|
||||||
return null;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,15 +65,10 @@ public class YAMLSource extends FileConfigSource<MemorySection, Map<String, Obje
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull MemorySection section() {
|
public @NotNull SourcedSection section() {
|
||||||
return Objects.requireNonNull(this.rootSection, "Root section is not initialized.");
|
return Objects.requireNonNull(this.rootSection, "Root section is not initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public char separator() {
|
|
||||||
return holder().options().get(StandardOptions.PATH_SEPARATOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull LoaderOptions loaderOptions() {
|
public @NotNull LoaderOptions loaderOptions() {
|
||||||
return holder().options().get(YAMLOptions.LOADER);
|
return holder().options().get(YAMLOptions.LOADER);
|
||||||
}
|
}
|
||||||
@@ -114,9 +109,9 @@ public class YAMLSource extends FileConfigSource<MemorySection, Map<String, Obje
|
|||||||
@Override
|
@Override
|
||||||
public void save() throws Exception {
|
public void save() throws Exception {
|
||||||
CommentedYAMLWriter writer = new CommentedYAMLWriter(
|
CommentedYAMLWriter writer = new CommentedYAMLWriter(
|
||||||
String.valueOf(this.separator()),
|
String.valueOf(this.pathSeparator()),
|
||||||
dumperOptions().getIndent(),
|
dumperOptions().getIndent(),
|
||||||
holder.options().get(CommentableOptions.COMMENT_EMPTY_VALUE)
|
holder.option(CommentableOptions.COMMENT_EMPTY_VALUE)
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
fileWriter(w -> w.write(writer.saveToString(this)));
|
fileWriter(w -> w.write(writer.saveToString(this)));
|
||||||
@@ -135,18 +130,18 @@ public class YAMLSource extends FileConfigSource<MemorySection, Map<String, Obje
|
|||||||
return this.saveToString(section());
|
return this.saveToString(section());
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NotNull MemorySection loadFromString(@NotNull String data) throws Exception {
|
public @NotNull SourcedSection loadFromString(@NotNull String data) throws Exception {
|
||||||
MappingNode mappingNode;
|
MappingNode mappingNode;
|
||||||
try (Reader reader = new UnicodeReader(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)))) {
|
try (Reader reader = new UnicodeReader(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)))) {
|
||||||
Node rawNode = this.yaml.compose(reader);
|
Node rawNode = this.yaml.compose(reader);
|
||||||
mappingNode = (MappingNode) rawNode;
|
mappingNode = (MappingNode) rawNode;
|
||||||
}
|
}
|
||||||
if (mappingNode == null) return MemorySection.root(this);
|
if (mappingNode == null) return SourcedSection.root(this);
|
||||||
|
|
||||||
|
|
||||||
Map<String, Object> map = new LinkedHashMap<>();
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
this.constructMap(mappingNode, map);
|
this.constructMap(mappingNode, map);
|
||||||
return MemorySection.root(this, map);
|
return SourcedSection.root(this, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void constructMap(@NotNull MappingNode mappingNode, @NotNull Map<String, Object> section) {
|
private void constructMap(@NotNull MappingNode mappingNode, @NotNull Map<String, Object> section) {
|
||||||
@@ -175,7 +170,7 @@ public class YAMLSource extends FileConfigSource<MemorySection, Map<String, Obje
|
|||||||
public String serializeValue(@NotNull String key, @NotNull Object value) {
|
public String serializeValue(@NotNull String key, @NotNull Object value) {
|
||||||
Map<String, Object> map = new LinkedHashMap<>();
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
map.put(key, value);
|
map.put(key, value);
|
||||||
return saveToString(MemorySection.root(this, map));
|
return saveToString(SourcedSection.root(this, map));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -193,46 +188,17 @@ public class YAMLSource extends FileConfigSource<MemorySection, Map<String, Obje
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable String getInlineComment(@NotNull String key) {
|
public @Nullable String getInlineComment(@NotNull String key) {
|
||||||
String comment = getInlineComment(key, null);
|
return Commentable.getInlineComment(holder(), key);
|
||||||
if (comment != null) return comment;
|
|
||||||
|
|
||||||
String sep = String.valueOf(separator());
|
|
||||||
|
|
||||||
// If the comment is not found, try to get the comment from the parent section
|
|
||||||
String[] keys = key.split(sep);
|
|
||||||
if (keys.length == 1) return null;
|
|
||||||
|
|
||||||
// Try every possible parent key&child key combination
|
|
||||||
for (int i = 1; i < keys.length; i++) {
|
|
||||||
String parentKey = String.join(sep, Arrays.copyOfRange(keys, 0, i));
|
|
||||||
String childKey = String.join(sep, Arrays.copyOfRange(keys, i, keys.length));
|
|
||||||
comment = getInlineComment(childKey, parentKey);
|
|
||||||
if (comment != null) return comment;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable String getInlineComment(@NotNull String key, @Nullable String sectionKey) {
|
|
||||||
Map<String, String> pathComment = holder().metadata(key).get(CommentableMeta.INLINE);
|
|
||||||
if (pathComment == null || pathComment.isEmpty()) return null;
|
|
||||||
if (sectionKey == null) return pathComment.get(null);
|
|
||||||
|
|
||||||
for (Map.Entry<String, String> entry : pathComment.entrySet()) {
|
|
||||||
if (entry.getKey().equals(sectionKey)) return entry.getValue();
|
|
||||||
Pattern pattern = Pattern.compile(entry.getKey().replace(".", "\\.").replace("*", ".*"));
|
|
||||||
if (pattern.matcher(sectionKey).matches()) return entry.getValue();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable List<String> getHeaderComments(@Nullable String key) {
|
public @Nullable List<String> getHeaderComments(@Nullable String key) {
|
||||||
return holder().metadata(key).get(CommentableMeta.HEADER);
|
return Commentable.getHeaderComments(holder(), key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable List<String> getFooterComments(@Nullable String key) {
|
public @Nullable List<String> getFooterComments(@Nullable String key) {
|
||||||
return holder().metadata(key).get(CommentableMeta.FOOTER);
|
return Commentable.getFooterComments(holder(), key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class YamlRepresenter extends Representer {
|
public static class YamlRepresenter extends Representer {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package sample;
|
|||||||
|
|
||||||
import cc.carm.lib.configuration.Configuration;
|
import cc.carm.lib.configuration.Configuration;
|
||||||
import cc.carm.lib.configuration.annotation.ConfigPath;
|
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||||
|
import cc.carm.lib.configuration.annotation.FooterComments;
|
||||||
import cc.carm.lib.configuration.annotation.HeaderComments;
|
import cc.carm.lib.configuration.annotation.HeaderComments;
|
||||||
import cc.carm.lib.configuration.annotation.InlineComment;
|
import cc.carm.lib.configuration.annotation.InlineComment;
|
||||||
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||||
@@ -19,6 +20,8 @@ public interface SampleConfig extends Configuration {
|
|||||||
@HeaderComments("Server configurations") // Header comment
|
@HeaderComments("Server configurations") // Header comment
|
||||||
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class);
|
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class);
|
||||||
|
|
||||||
|
@HeaderComments({"[ UUID >-----------------------------------", "A lot of UUIDs"})
|
||||||
|
@FooterComments("[ UUID >-----------------------------------")
|
||||||
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
|
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
|
||||||
.parse(UUID::fromString).serialize(UUID::toString)
|
.parse(UUID::fromString).serialize(UUID::toString)
|
||||||
.defaults(
|
.defaults(
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class SampleTest {
|
|||||||
// 1. Make a configuration provider from a file.
|
// 1. Make a configuration provider from a file.
|
||||||
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/config.yml")
|
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/config.yml")
|
||||||
.resourcePath("configs/sample.yml")
|
.resourcePath("configs/sample.yml")
|
||||||
.indent(4) // Optional: Set the indentation of the configuration file.
|
.indent(2) // Optional: Set the indentation of the configuration file.
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 2. Initialize the configuration classes or instances.
|
// 2. Initialize the configuration classes or instances.
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
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;
|
||||||
import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory;
|
import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory;
|
||||||
|
import cc.carm.lib.configuration.source.yaml.YAMLSource;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -14,13 +16,12 @@ public class YamlTests {
|
|||||||
@Test
|
@Test
|
||||||
public void test() {
|
public void test() {
|
||||||
|
|
||||||
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/tests.yml")
|
ConfigurationHolder<YAMLSource> holder = YAMLConfigFactory.from("target/tests.yml")
|
||||||
.resourcePath("configs/sample.yml").build();
|
.resourcePath("configs/sample.yml").build();
|
||||||
|
|
||||||
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) -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user