mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 18:48:20 +08:00
Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| f2c9750c35 | |||
| c0f0e989c6 | |||
| 6490edf65e | |||
| 14a5f41649 | |||
| 0b16ae08db | |||
| 020d0df3a3 | |||
| f3bdff1560 | |||
| d3ec93cef2 | |||
| 96ed604cd9 | |||
| eff91b0496 | |||
| 8db7ecf539 | |||
| 1b49e466a9 | |||
| 6df9f2ce42 | |||
| aedc6cb439 | |||
| c79b94b719 | |||
| 3a3533ce81 | |||
| 4cdd184fe6 | |||
| 18199cd1cf | |||
| 47e2a4854c | |||
| 5b95824bb0 | |||
| 2cda4ae11c | |||
| e840f6bd50 | |||
| 90407dcd0d | |||
| 79f59bafe6 | |||
| 05ff61a9d9 | |||
| c68d2371ee | |||
| 2093091923 | |||
| 45ca8b02d4 | |||
| 7f09e6cdf7 | |||
| 205db6e5b9 | |||
| 8ac1faf300 | |||
| 374a6198d8 | |||
| 6ee23ded3b | |||
| 0c7cc5c156 | |||
| 4988bb15ae | |||
| 8b24e8dbff | |||
| ce9d858f6c | |||
| efc837b6f0 | |||
| 44e864ef0f | |||
| ce33d8ff9d | |||
| de70c11f21 | |||
| a182b9af99 | |||
| c56499c0e9 | |||
| c9150ecb50 | |||
| 5075a4c96d | |||
| 30a9f3634f | |||
| 022acb97a5 | |||
| 97eda55ec7 | |||
| 7fe77b1363 | |||
| 9f9f02df51 | |||
| 41a53b1c89 | |||
| 9626ed8f49 | |||
| 119f93f234 | |||
| f2b6069389 | |||
| 6e2808380a | |||
| 3ca44aa233 | |||
| 4de9ebd118 | |||
| 217cd66bfd | |||
| f1f9a55e01 | |||
| 530d7b3391 | |||
| 2d381c3ce4 | |||
| 08626ae8c2 | |||
| 7fe2e74582 | |||
| a49180bb1d | |||
| 69a4b5e918 | |||
| 27c3c7803c | |||
| c95b1ae4d4 | |||
| c7f928686d | |||
| 4ab84cfc71 | |||
| d347d3de23 | |||
| 2b48e3146b | |||
| d35958a943 | |||
| b7990d4de3 | |||
| f01a15a19d | |||
| 85625bfe05 | |||
| cb59471f9b | |||
| 112803342a | |||
| fc1792113d | |||
| da3d4d1fd2 | |||
| b912ea369c | |||
| 2df33e3458 | |||
| d041828717 | |||
| c7fb51d6b2 | |||
| 2ef1471f59 | |||
| 20a6eaefcd | |||
| e419e2498f | |||
| 6ef4f9aebd | |||
| 21a8906ad9 | |||
| c91794c611 | |||
| 3769cf4438 | |||
| 34bc7601ee | |||
| 0d19dc236e | |||
| 9647591b0d | |||
| 97db8204dc | |||
| 48050a52a2 | |||
| e42de0eee7 | |||
| 814dae2278 | |||
| db19531c13 | |||
| d0af3e436f | |||
| 1252baab76 | |||
| 83037baf10 | |||
| adf90a983b | |||
| 02d816cd8d | |||
| 5d5626bff2 | |||
| 363c1e7ed8 | |||
| 7884d49309 | |||
| b77e64e6e6 | |||
| 505b86ec9c | |||
| 374f646f9e | |||
| aa50345329 | |||
| db8b227317 | |||
| 60eed8a14d | |||
| d41630be72 | |||
| 1c3a2b01ea | |||
| 1ede74c692 | |||
| 67456a8aac | |||
| f422900fb5 | |||
| b3e0007eba | |||
| 89c8ec8adf | |||
| e8f49bf8bc | |||
| 8ac980fdbb | |||
| 9f2e4bc0cb | |||
| ae16d131c3 | |||
| bd8470a6e8 | |||
| 6af2d3fef5 | |||
| 00f83002c5 | |||
| 0aa548cbbf | |||
| 9c0ed1c5c2 | |||
| bb0998cbac | |||
| 1eb16f00b6 | |||
| 5ccf63b423 | |||
| f1c0c74574 | |||
| 8ee074474c | |||
| 27a68ead7c | |||
| c6cce5208f | |||
| bc67de06f6 | |||
| b668794f5d | |||
| 07424284b7 | |||
| 81e024e309 | |||
| 763fc7c758 | |||
| 56557221a4 | |||
| e4435bf883 | |||
| eee4a278d9 | |||
| 3a0a8e79b9 | |||
| 3b2b1b27cc | |||
| d84ea1b7da | |||
| a1f2cdca04 | |||
| c52183aadd | |||
| d71aabad2d |
@@ -0,0 +1,16 @@
|
||||
# 欢迎使用 EasyConfiguration !
|
||||
|
||||
这个项目刚刚创建,详细的Javadoc与开发指南还在补充,请给我一点时间~
|
||||
|
||||
|
||||
## 基本定义
|
||||
|
||||
Value: 实际配置的单例值。
|
||||
|
||||
Manifest: 用于描述值基本配置的对象。
|
||||
|
||||
Provider: 用于提供配置文件的接口。
|
||||
|
||||
Wrapper: 用于包装配置文件的接口。
|
||||
|
||||
Initializer: 用于初始化的接口
|
||||
@@ -1,3 +0,0 @@
|
||||
# 欢迎使用 EasyConfiguration !
|
||||
|
||||
这个项目刚刚创建,详细的Javadoc与开发指南还在补充,请给我一点时间~
|
||||
@@ -29,11 +29,11 @@ jobs:
|
||||
steps:
|
||||
# Checkout the repository to the GitHub Actions runner
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
|
||||
- name: Run Codacy Analysis CLI
|
||||
uses: codacy/codacy-analysis-cli-action@d840f886c4bd4edc059706d09c6a1586111c540b
|
||||
uses: codacy/codacy-analysis-cli-action@09916000460adeeedc96b9704f86deba53e2ad5d
|
||||
with:
|
||||
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
|
||||
# You can also omit the token and run the tools that support default configurations
|
||||
@@ -49,6 +49,6 @@ jobs:
|
||||
|
||||
# Upload the SARIF file generated in the previous step
|
||||
- name: Upload SARIF results file
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -38,11 +38,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -67,4 +67,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
@@ -16,9 +16,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
@@ -40,8 +40,8 @@ jobs:
|
||||
run: |
|
||||
rm -rf docs
|
||||
mkdir -vp docs
|
||||
cp -vrf core/target/apidocs/* docs/
|
||||
cp -vrf .documentation/JAVADOC-README.md docs/README.md
|
||||
cp -vrf core/target/reports/apidocs/* docs/
|
||||
cp -vrf .doc/JAVADOC-README.md docs/README.md
|
||||
|
||||
- name: "Generate the Javadoc sitemap"
|
||||
id: sitemap
|
||||
@@ -87,21 +87,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
server-id: ossrh
|
||||
server-id: central
|
||||
server-username: MAVEN_USERNAME
|
||||
server-password: MAVEN_PASSWORD
|
||||
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
|
||||
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
|
||||
|
||||
- name: "Central Deploy"
|
||||
run: mvn -B -Possrh deploy --file pom.xml -DskipTests
|
||||
run: mvn -B -Pcentral deploy --file pom.xml -DskipTests
|
||||
env:
|
||||
MAVEN_USERNAME: ${{ secrets.OSSRH_USER }}
|
||||
MAVEN_PASSWORD: ${{ secrets.OSSRH_PASS }}
|
||||
|
||||
@@ -15,9 +15,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: "Target Stage"
|
||||
run: mkdir staging && cp */target/*.jar staging
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Artifact
|
||||
path: staging
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
/.idea/
|
||||
.idea/
|
||||
**/target/
|
||||
**.iml
|
||||
@@ -6,6 +6,8 @@
|
||||
/___/ /___/
|
||||
```
|
||||
|
||||
README LANGUAGES [ [**English**](README.md) | [中文](README_CN.md) ]
|
||||
|
||||
# EasyConfiguration
|
||||
|
||||
[](https://github.com/CarmJos/EasyConfiguration/releases)
|
||||
@@ -15,29 +17,122 @@
|
||||

|
||||

|
||||
|
||||
轻松(做)配置,简单便捷的通用配置文件加载、读取与更新工具,可自定义配置格式。
|
||||
**Easy _(to make)_ Configurations!**
|
||||
|
||||
## 优势
|
||||
A simple, easy-to-use and universal solution for managing configuration files.
|
||||
Enjoy the ease of use with customizable formats for loading, reading, and updating your configuration files.
|
||||
|
||||
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
|
||||
- 支持复杂配置的手动序列化、反序列化。
|
||||
- 提供多种builder形式,快速构建 `ConfigValue<?>` 对象。
|
||||
- 支持通过注解规定配置对应的路径、注释等信息。
|
||||
## Features & Advantages
|
||||
|
||||
## 开发
|
||||
Supported [YAML](impl/yaml), [JSON](impl/json), [HOCON](impl/hocon) and [SQL](impl/sql) based configuration files
|
||||
format.
|
||||
|
||||
详细开发介绍请 [点击这里](.documentation/README.md) , JavaDoc(最新Release) 请 [点击这里](https://CarmJos.github.io/EasyConfiguration) 。
|
||||
- Class-based mechanism for initializing, loading, retrieving, and updating configuration files, ensuring convenience
|
||||
and efficiency.
|
||||
- Supports manual serialization and deserialization of complex configurations.
|
||||
- Offers multiple builder forms for rapid construction of `ConfigValue<?>` objects.
|
||||
- Enables specification of configuration paths, comments, and more via annotations.
|
||||
|
||||
### 示例代码
|
||||
## Development
|
||||
|
||||
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java) 查看部分代码演示,更多演示详见 [开发介绍](.documentation/README.md) 。
|
||||
For the latest JavaDoc release, [CLICK HERE](https://CarmJos.github.io/EasyConfiguration).
|
||||
|
||||
### 依赖方式
|
||||
For a detailed development guide, [CLICK HERE](.doc/README.md).
|
||||
|
||||
#### Maven 依赖
|
||||
### Code Samples
|
||||
|
||||
To quickly demonstrate the applicability of the project, here are a few practical demonstrations:
|
||||
- [Database configuration.](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java)
|
||||
- [Demonstration of all types of configuration instance classes.](demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java)
|
||||
|
||||
Check out all code demonstrations [HERE](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java).
|
||||
For more examples, see the [Development Guide](.doc/README.md).
|
||||
|
||||
```java
|
||||
@ConfigPath(root = true)
|
||||
@HeaderComments("Configurations for sample")
|
||||
public interface SampleConfig extends Configuration {
|
||||
|
||||
@InlineComment("Enabled?") // Inline comment
|
||||
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()
|
||||
.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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
public class Sample {
|
||||
public static void main(String[] args) {
|
||||
// 1. Make a configuration provider from a file.
|
||||
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.
|
||||
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() + ")!");
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Configurations for sample
|
||||
|
||||
enabled: true #Enabled?
|
||||
|
||||
# Server configurations
|
||||
port:
|
||||
|
||||
# [ UUID >-----------------------------------
|
||||
# A lot of UUIDs
|
||||
uuids:
|
||||
- 00000000-0000-0000-0000-000000000000
|
||||
- 00000000-0000-0000-0000-000000000001
|
||||
# [ UUID >-----------------------------------
|
||||
|
||||
info:
|
||||
# Configure your name!
|
||||
name: Joker
|
||||
how-old-are-you: 24
|
||||
```
|
||||
|
||||
### Dependencies
|
||||
|
||||
#### Maven Dependency
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
<summary>Remote Repository Configuration</summary>
|
||||
|
||||
```xml
|
||||
|
||||
@@ -45,26 +140,19 @@
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!--采用Maven中心库,安全稳定,但版本更新需要等待同步-->
|
||||
<!-- Using Maven Central Repository for secure and stable updates, though synchronization might be needed. -->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用github依赖库,实时更新,但需要配置 (推荐) -->
|
||||
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
|
||||
<id>EasyConfiguration</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用我的私人依赖库,简单方便,但可能因为变故而无法使用-->
|
||||
<id>carm-repo</id>
|
||||
<name>Carm's Repo</name>
|
||||
<url>https://repo.carm.cc/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
</project>
|
||||
```
|
||||
@@ -72,13 +160,13 @@
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
<summary>Generic Native Dependency</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<!--基础实现部分,需要自行实现“Provider”与“Wrapper”。-->
|
||||
<!-- Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”. -->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
@@ -86,7 +174,7 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于YAML文件的实现版本,可用于全部Java环境。-->
|
||||
<!-- YAML file-based implementation, compatible with all Java environments. -->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-yaml</artifactId>
|
||||
@@ -94,11 +182,10 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于JSON文件的实现版本,可用于全部Java环境。-->
|
||||
<!--需要注意的是,JSON不支持文件注释。-->
|
||||
<!-- JSON file-based implementation, compatible with all Java environments. -->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-json</artifactId>
|
||||
<artifactId>easyconfiguration-gson</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
@@ -109,66 +196,67 @@
|
||||
|
||||
</details>
|
||||
|
||||
#### Gradle 依赖
|
||||
#### Gradle Dependency
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
<summary>Remote Repository Configuration</summary>
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// 采用Maven中心库,安全稳定,但版本更新需要等待同步
|
||||
// Using Maven Central Repository for secure and stable updates, though synchronization might be needed.
|
||||
mavenCentral()
|
||||
|
||||
// 采用github依赖库,实时更新,但需要配置 (推荐)
|
||||
// Using GitHub dependencies for real-time updates, configuration required (recommended).
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
||||
|
||||
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
|
||||
maven { url 'https://repo.carm.cc/repository/maven-public/' }
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
<summary>Generic Native Dependency</summary>
|
||||
|
||||
```groovy
|
||||
|
||||
dependencies {
|
||||
|
||||
//基础实现部分,需要自行实现“Provider”与“Wrapper”。
|
||||
// Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”.
|
||||
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
|
||||
|
||||
//基于YAML文件的实现版本,可用于全部Java环境。
|
||||
// YAML file-based implementation, compatible with all Java environments.
|
||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
||||
|
||||
//基于JSON文件的实现版本,可用于全部Java环境。
|
||||
//需要注意的是,JSON不支持文件注释。
|
||||
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
|
||||
// JSON file-based implementation, compatible with all Java environments.
|
||||
api "cc.carm.lib:easyconfiguration-gson:[LATEST RELEASE]"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 衍生项目
|
||||
## Derived Projects
|
||||
|
||||
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos )
|
||||
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos)
|
||||
|
||||
EasyConfiguration for MineCraft!
|
||||
开始在 MineCraft 相关服务器平台上轻松(做)配置吧!
|
||||
Easily manage configurations on MineCraft-related server platforms.
|
||||
|
||||
目前支持 BungeeCord, Bukkit(Spigot) 服务端,后续将支持更多平台。
|
||||
Currently, it supports BungeeCord, Velocity, Bukkit (Spigot) servers,
|
||||
with more platforms to be supported soon.
|
||||
|
||||
## 支持与捐赠
|
||||
## Support and Donation
|
||||
|
||||
若您觉得本插件做的不错,您可以通过捐赠支持我!
|
||||
If you appreciate this plugin, consider supporting me with a donation!
|
||||
|
||||
感谢您对开源项目的支持!
|
||||
Thank you for supporting open-source projects!
|
||||
|
||||
<img height=25% width=25% src="https://raw.githubusercontent.com/CarmJos/CarmJos/main/img/donate-code.jpg" alt=""/>
|
||||
Many thanks to Jetbrains for kindly providing a license for us to work on this and other open-source projects.
|
||||
|
||||
## 开源协议
|
||||
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/EasyConfiguration)
|
||||
|
||||
本项目源码采用 [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html) 开源协议。
|
||||
## Open Source License
|
||||
|
||||
This project's source code is licensed under
|
||||
the [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html).
|
||||
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
```text
|
||||
____ _____ ____ __ _
|
||||
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
|
||||
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
|
||||
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
|
||||
/___/ /___/
|
||||
```
|
||||
|
||||
README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
||||
|
||||
# EasyConfiguration
|
||||
|
||||
[](https://github.com/CarmJos/EasyConfiguration/releases)
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0.html)
|
||||
[](https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml)
|
||||
[](https://www.codefactor.io/repository/github/carmjos/easyconfiguration)
|
||||

|
||||

|
||||
|
||||
**轻松(做)配置!**
|
||||
|
||||
一款简单便捷的通用配置文件加载、读取与更新工具,可自定义配置的格式。
|
||||
|
||||
## 特性 & 优势
|
||||
|
||||
支持 [YAML](impl/yaml), [JSON](impl/json), [HOCON](impl/hocon) 和 [SQL](impl/sql) 等多种配置文件格式。
|
||||
|
||||
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
|
||||
- 支持复杂配置的手动序列化、反序列化。
|
||||
- 提供多种builder形式,快速构建 `ConfigValue<?>` 对象。
|
||||
- 支持通过注解规定配置对应的路径、注释等信息。
|
||||
|
||||
## 开发
|
||||
|
||||
详细开发介绍请 [点击这里](.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/tests/conf/DemoConfiguration.java)
|
||||
|
||||
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo) 直接查看现有的代码演示,更多复杂情况演示详见 [开发介绍](.doc/README.md) 。
|
||||
|
||||
|
||||
|
||||
```java
|
||||
@ConfigPath(root = true)
|
||||
@HeaderComments("Configurations for sample")
|
||||
public interface SampleConfig extends Configuration {
|
||||
|
||||
@InlineComment("Enabled?") // 行后注释
|
||||
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()
|
||||
.parse(UUID::fromString).serialize(UUID::toString)
|
||||
.defaults(
|
||||
UUID.fromString("00000000-0000-0000-0000-000000000000"),
|
||||
UUID.fromString("00000000-0000-0000-0000-000000000001")
|
||||
).build();
|
||||
|
||||
interface INFO extends Configuration {
|
||||
|
||||
@HeaderComments("Configure your name!") // Header comment
|
||||
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
|
||||
|
||||
@ConfigPath("how-old-are-you") // 自定义路径
|
||||
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```java
|
||||
public class Sample {
|
||||
public static void main(String[] args) {
|
||||
// 1. 生成一个 “holder” 用于给配置类提供源配置的文件。
|
||||
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/config.yml")
|
||||
.resourcePath("configs/sample.yml")
|
||||
.indent(4) // Optional: Set the indentation of the configuration file.
|
||||
.build();
|
||||
|
||||
// 2. 通过 “holder” 初始化配置类或配置实例。
|
||||
holder.initialize(SampleConfig.class);
|
||||
// 3. 现在可以享受快捷方便的配置文件使用方式了~
|
||||
System.out.println("Enabled? -> " + SampleConfig.ENABLED.resolve());
|
||||
SampleConfig.ENABLED.set(false);
|
||||
System.out.println("And now? -> " + SampleConfig.ENABLED.resolve());
|
||||
// p.s. 在本示例里的更改未保存,因此启用值在下次运行中仍将为 true。
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Configurations for sample
|
||||
|
||||
enabled: true #Enabled?
|
||||
|
||||
# Server configurations
|
||||
port:
|
||||
|
||||
# [ UUID >-----------------------------------
|
||||
# A lot of UUIDs
|
||||
uuids:
|
||||
- 00000000-0000-0000-0000-000000000000
|
||||
- 00000000-0000-0000-0000-000000000001
|
||||
# [ UUID >-----------------------------------
|
||||
|
||||
info:
|
||||
# Configure your name!
|
||||
name: Joker
|
||||
how-old-are-you: 24
|
||||
```
|
||||
|
||||
### 依赖方式
|
||||
|
||||
#### Maven 依赖
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!--采用Maven中心库,安全稳定,但版本更新需要等待同步-->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用github依赖库,实时更新,但需要配置 (推荐) -->
|
||||
<id>EasyConfiguration</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用我的私人依赖库,简单方便,但可能因为变故而无法使用-->
|
||||
<id>carm-repo</id>
|
||||
<name>Carm's Repo</name>
|
||||
<url>https://repo.carm.cc/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
</project>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<!--基础实现部分,需要自行实现“Provider”与“Wrapper”。-->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于YAML文件的实现版本,可用于全部Java环境。-->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-yaml</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于JSON文件的实现版本,可用于全部Java环境。-->
|
||||
<!--需要注意的是,JSON不支持文件注释。-->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-gson</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-hocon</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-sql</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Gradle 依赖
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// 采用Maven中心库,安全稳定,但版本更新需要等待同步
|
||||
mavenCentral()
|
||||
|
||||
// 采用github依赖库,实时更新,但需要配置 (推荐)
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
||||
|
||||
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
|
||||
maven { url 'https://repo.carm.cc/repository/maven-public/' }
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
|
||||
```groovy
|
||||
|
||||
dependencies {
|
||||
|
||||
//基础实现部分,需要自行实现“Provider”与“Wrapper”。
|
||||
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
|
||||
|
||||
//基于YAML文件的实现版本,可用于全部Java环境。
|
||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
||||
|
||||
//基于JSON文件的实现版本,可用于全部Java环境。
|
||||
//需要注意的是,JSON不支持文件注释。
|
||||
api "cc.carm.lib:easyconfiguration-gson:[LATEST RELEASE]"
|
||||
|
||||
api "cc.carm.lib:easyconfiguration-hocon:[LATEST RELEASE]"
|
||||
|
||||
api "cc.carm.lib:easyconfiguration-sql:[LATEST RELEASE]"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 衍生项目
|
||||
|
||||
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos )
|
||||
|
||||
EasyConfiguration for MineCraft!
|
||||
开始在 MineCraft 相关服务器平台上轻松(做)配置吧!
|
||||
|
||||
目前支持 BungeeCord, Bukkit(Spigot) 服务端,后续将支持更多平台。
|
||||
|
||||
## 支持与捐赠
|
||||
|
||||
若您觉得本插件做的不错,您可以通过捐赠支持我!
|
||||
|
||||
感谢您对开源项目的支持!
|
||||
|
||||
万分感谢 Jetbrains 为我们提供了从事此项目和其他开源项目的许可!
|
||||
|
||||
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/EasyConfiguration)
|
||||
|
||||
## 开源协议
|
||||
|
||||
本项目源码采用 [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html) 开源协议。
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>3.6.0</version>
|
||||
<version>4.0.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package cc.carm.lib.configuration;
|
||||
|
||||
/**
|
||||
* The root interface of the configuration file interfaces,
|
||||
* which is used to label a class as a configuration.
|
||||
*/
|
||||
public interface Configuration { }
|
||||
@@ -0,0 +1,79 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Value adapter, used to convert the value of the configuration file into the objects.
|
||||
*
|
||||
* @param <TYPE> The type of the target value
|
||||
*/
|
||||
public class ValueAdapter<TYPE>
|
||||
implements ValueSerializer<TYPE>, ValueParser<TYPE> {
|
||||
|
||||
protected final @NotNull ValueType<TYPE> type;
|
||||
protected @Nullable ValueSerializer<TYPE> serializer;
|
||||
protected @Nullable ValueParser<TYPE> deserializer;
|
||||
|
||||
public ValueAdapter(@NotNull ValueType<TYPE> type) {
|
||||
this(type, null, null);
|
||||
}
|
||||
|
||||
public ValueAdapter(@NotNull ValueType<TYPE> type,
|
||||
@Nullable ValueSerializer<TYPE> serializer,
|
||||
@Nullable ValueParser<TYPE> parser) {
|
||||
this.type = type;
|
||||
this.serializer = serializer;
|
||||
this.deserializer = parser;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<TYPE> type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public @Nullable ValueSerializer<TYPE> serializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
public @Nullable ValueParser<TYPE> parser() {
|
||||
return deserializer;
|
||||
}
|
||||
|
||||
public ValueAdapter<TYPE> serializer(@Nullable ValueSerializer<TYPE> serializer) {
|
||||
this.serializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ValueAdapter<TYPE> parser(@Nullable ValueParser<TYPE> deserializer) {
|
||||
this.deserializer = deserializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object serialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull TYPE value) throws Exception {
|
||||
if (serializer == null) throw new UnsupportedOperationException("Serializer is not supported");
|
||||
return serializer.serialize(holder, type, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TYPE parse(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull Object value) throws Exception {
|
||||
if (deserializer == null) throw new UnsupportedOperationException("Deserializer is not supported");
|
||||
return deserializer.parse(holder, type, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof ValueAdapter)) return false;
|
||||
ValueAdapter<?> that = (ValueAdapter<?>) o;
|
||||
return Objects.equals(type, that.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class ValueAdapterRegistry {
|
||||
|
||||
protected final Set<ValueAdapter<?>> adapters = new HashSet<>();
|
||||
|
||||
public <FROM, TO> void register(@NotNull Class<FROM> from, @NotNull Class<TO> to,
|
||||
@Nullable DataFunction<FROM, TO> parser,
|
||||
@Nullable DataFunction<TO, FROM> serializer) {
|
||||
register(ValueType.of(from), ValueType.of(to), parser, serializer);
|
||||
}
|
||||
|
||||
public <FROM, TO> void register(@NotNull ValueType<FROM> from, @NotNull ValueType<TO> to,
|
||||
@Nullable DataFunction<FROM, TO> parser,
|
||||
@Nullable DataFunction<TO, FROM> serializer) {
|
||||
ValueAdapter<FROM> fromAdapter = adapterOf(from);
|
||||
if (fromAdapter == null) throw new IllegalArgumentException("No adapter for type " + from);
|
||||
register(to,
|
||||
serializer == null ? null : (provider, type, value) -> fromAdapter.serialize(provider, from, serializer.handle(value)),
|
||||
parser == null ? null : (provider, type, data) -> parser.handle(fromAdapter.parse(provider, from, data))
|
||||
);
|
||||
}
|
||||
|
||||
public void register(@NotNull ValueAdapter<?>... adapter) {
|
||||
adapters.addAll(Arrays.asList(adapter));
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull Class<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||
register(ValueType.of(type), serializer);
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||
ValueAdapter<T> existing = adapterOf(type);
|
||||
if (existing != null) {
|
||||
existing.serializer(serializer);
|
||||
} else {
|
||||
register(new ValueAdapter<>(type, serializer, null));
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull Class<T> type, @NotNull ValueParser<T> deserializer) {
|
||||
register(ValueType.of(type), deserializer);
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull ValueType<T> type, @NotNull ValueParser<T> deserializer) {
|
||||
ValueAdapter<T> existing = adapterOf(type);
|
||||
if (existing != null) {
|
||||
existing.parser(deserializer);
|
||||
} else {
|
||||
register(new ValueAdapter<>(type, null, deserializer));
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void register(@NotNull ValueType<T> type, @Nullable ValueSerializer<T> serializer, @Nullable ValueParser<T> deserializer) {
|
||||
if (serializer == null && deserializer == null) return;
|
||||
ValueAdapter<T> existing = adapterOf(type);
|
||||
if (existing != null) {
|
||||
if (serializer != null) existing.serializer(serializer);
|
||||
if (deserializer != null) existing.parser(deserializer);
|
||||
} else {
|
||||
register(new ValueAdapter<>(type, serializer, deserializer));
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(@NotNull Class<?> type) {
|
||||
unregister(ValueType.of(type));
|
||||
}
|
||||
|
||||
public void unregister(@NotNull ValueType<?> type) {
|
||||
adapters.removeIf(adapter -> adapter.type().equals(type));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> @Nullable ValueAdapter<T> adapterOf(@NotNull ValueType<T> type) {
|
||||
ValueAdapter<?> matched = adapters.stream().filter(adapter -> adapter.type().equals(type)).findFirst().orElse(null);
|
||||
if (matched != null) return (ValueAdapter<T>) matched;
|
||||
|
||||
// If no adapter found, try to find the adapter for the super type
|
||||
return (ValueAdapter<T>) adapters.stream()
|
||||
.filter(adapter -> adapter.type().isSubtypeOf(type))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public <T> ValueAdapter<T> adapterOf(@NotNull T value) {
|
||||
return adapterOf(ValueType.of(value));
|
||||
}
|
||||
|
||||
public <T> ValueAdapter<T> adapterOf(@NotNull Class<T> type) {
|
||||
return adapterOf(ValueType.of(type));
|
||||
}
|
||||
|
||||
@Contract("_,_,null -> null")
|
||||
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||
return deserialize(holder, ValueType.of(type), source);
|
||||
}
|
||||
|
||||
@Contract("_,_,null -> null")
|
||||
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type, @Nullable Object source) throws Exception {
|
||||
if (source == null) return null; // Null check
|
||||
if (type.isInstance(source)) return type.cast(source); // Not required to deserialize
|
||||
ValueAdapter<T> adapter = adapterOf(type);
|
||||
if (adapter == null) throw new RuntimeException("No adapter for type " + type);
|
||||
return adapter.parse(holder, type, source);
|
||||
}
|
||||
|
||||
@Contract("_,null -> null")
|
||||
public <T> Object serialize(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception {
|
||||
if (value == null) return null; // Null check
|
||||
ValueType<T> type = ValueType.of(value);
|
||||
ValueAdapter<T> adapter = adapterOf(type);
|
||||
if (adapter == null) return value; // No adapters, try to return the original value
|
||||
return adapter.serialize(holder, type, value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Value deserializer, convert base data to target value.
|
||||
*
|
||||
* @param <TYPE> The type of target value
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ValueParser<TYPE> {
|
||||
|
||||
TYPE parse(
|
||||
@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull ValueType<? super TYPE> type, @NotNull Object data
|
||||
) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Value serializer, convert target value to base data.
|
||||
*
|
||||
* @param <TYPE> The type of value
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ValueSerializer<TYPE> {
|
||||
|
||||
Object serialize(
|
||||
@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull ValueType<? super TYPE> type, @NotNull TYPE value
|
||||
) throws Exception;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package cc.carm.lib.configuration.adapter;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Used to get the generic type.
|
||||
*/
|
||||
public abstract class ValueType<T> {
|
||||
|
||||
public static final ValueType<String> STRING = ofPrimitiveType(String.class);
|
||||
public static final ValueType<Integer> INTEGER = ofPrimitiveType(Integer.class);
|
||||
public static final ValueType<Integer> INTEGER_TYPE = ofPrimitiveType(int.class);
|
||||
public static final ValueType<Long> LONG = ofPrimitiveType(Long.class);
|
||||
public static final ValueType<Long> LONG_TYPE = ofPrimitiveType(long.class);
|
||||
public static final ValueType<Double> DOUBLE = ofPrimitiveType(Double.class);
|
||||
public static final ValueType<Double> DOUBLE_TYPE = ofPrimitiveType(double.class);
|
||||
public static final ValueType<Float> FLOAT = ofPrimitiveType(Float.class);
|
||||
public static final ValueType<Float> FLOAT_TYPE = ofPrimitiveType(float.class);
|
||||
public static final ValueType<Boolean> BOOLEAN = ofPrimitiveType(Boolean.class);
|
||||
public static final ValueType<Boolean> BOOLEAN_TYPE = ofPrimitiveType(boolean.class);
|
||||
public static final ValueType<Byte> BYTE = ofPrimitiveType(Byte.class);
|
||||
public static final ValueType<Byte> BYTE_TYPE = ofPrimitiveType(byte.class);
|
||||
public static final ValueType<Short> SHORT = ofPrimitiveType(Short.class);
|
||||
public static final ValueType<Short> SHORT_TYPE = ofPrimitiveType(short.class);
|
||||
public static final ValueType<Character> CHAR = ofPrimitiveType(Character.class);
|
||||
public static final ValueType<Character> CHAR_TYPE = ofPrimitiveType(char.class);
|
||||
|
||||
public static final ValueType<?>[] PRIMITIVE_TYPES = {
|
||||
STRING, INTEGER, LONG, DOUBLE, FLOAT, BOOLEAN, BYTE, SHORT, CHAR,
|
||||
INTEGER_TYPE, LONG_TYPE, DOUBLE_TYPE, FLOAT_TYPE, BOOLEAN_TYPE, BYTE_TYPE, SHORT_TYPE, CHAR_TYPE
|
||||
};
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> ValueType<T> of(@NotNull T value) {
|
||||
return of((Class<T>) value.getClass());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> ValueType<T> of(final Type type) {
|
||||
if (type == null) throw new NullPointerException("Type cannot be null");
|
||||
if (type instanceof Class<?>) { // Try handle primitive types
|
||||
Class<?> clazz = (Class<?>) type;
|
||||
for (ValueType<?> valueType : PRIMITIVE_TYPES) {
|
||||
if (valueType.getRawType() == clazz) {
|
||||
return (ValueType<T>) valueType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ValueType<T>(type) {
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> ValueType<T> of(final Class<T> clazz) {
|
||||
return of((Type) clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generic type of the complex type.
|
||||
*
|
||||
* @param rawType The raw type
|
||||
* @param types The type arguments
|
||||
* @param <T> The type
|
||||
* @return The {@link ValueType}
|
||||
*/
|
||||
public static <T> ValueType<T> of(final Class<?> rawType, final Type... types) {
|
||||
ParameterizedType parameterizedType = new ParameterizedType() {
|
||||
@Override
|
||||
public @NotNull Type @NotNull [] getActualTypeArguments() {
|
||||
return types;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Type getRawType() {
|
||||
return rawType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getOwnerType() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
return of(parameterizedType);
|
||||
}
|
||||
|
||||
private static <T> ValueType<T> ofPrimitiveType(Class<T> clazz) {
|
||||
return new ValueType<T>(clazz) {
|
||||
};
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
|
||||
protected ValueType() {
|
||||
this.type = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
|
||||
}
|
||||
|
||||
private ValueType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public boolean isSubtypeOf(Class<?> target) {
|
||||
Class<?> rawType = getRawType();
|
||||
return target.isAssignableFrom(rawType);
|
||||
}
|
||||
|
||||
public boolean isSubtypeOf(ValueType<?> target) {
|
||||
return target.isSubtypeOf(getRawType());
|
||||
}
|
||||
|
||||
public boolean isInstance(Object obj) {
|
||||
return obj != null && getRawType().isInstance(obj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 提取当前 ValueType 的原始类型(Class 对象)。
|
||||
*
|
||||
* @return 对应的 Class 对象
|
||||
* @throws IllegalStateException 如果无法提取出原始类型
|
||||
*/
|
||||
public Class<?> getRawType() {
|
||||
if (type instanceof Class<?>) {
|
||||
return (Class<?>) type;
|
||||
}
|
||||
if (type instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) type;
|
||||
Type raw = pt.getRawType();
|
||||
if (raw instanceof Class<?>) {
|
||||
return (Class<?>) raw;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Unsupported type: " + type);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T cast(Object obj) {
|
||||
if (!isInstance(obj)) {
|
||||
throw new ClassCastException("Cannot cast object " + obj + " to type " + this);
|
||||
}
|
||||
return (T) obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (type instanceof Class<?>) {
|
||||
return ((Class<?>) type).getName();
|
||||
}
|
||||
if (type instanceof ParameterizedType) {
|
||||
ParameterizedType pt = (ParameterizedType) type;
|
||||
Type raw = pt.getRawType();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(raw.getTypeName());
|
||||
sb.append('<');
|
||||
Type[] args = pt.getActualTypeArguments();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(args[i].getTypeName());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
return type.getTypeName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj instanceof ValueType) {
|
||||
return Objects.equals(type, ((ValueType<?>) obj).type);
|
||||
}
|
||||
if (obj instanceof Type) {
|
||||
return Objects.equals(type, obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package cc.carm.lib.configuration.adapter.strandard;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PrimitiveAdapter<T> extends ValueAdapter<T> {
|
||||
|
||||
public static final String[] TRUE_VALUES = new String[]{
|
||||
"true", "yes", "on", "1", "enabled", "enable", "active"
|
||||
};
|
||||
|
||||
public static final String[] FALSE_VALUES = new String[]{
|
||||
"false", "no", "off", "0", "disabled", "disable", "inactive"
|
||||
};
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static ValueAdapter<Enum<?>> ofEnum() {
|
||||
ValueAdapter<Enum<?>> adapter = new ValueAdapter<>(new ValueType<Enum<?>>() {
|
||||
});
|
||||
adapter.parser((provider, type, data) -> Enum.valueOf((Class<Enum>) type.getRawType(), data.toString()));
|
||||
adapter.serializer((provider, type, value) -> value.name());
|
||||
return adapter;
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<String> ofString() {
|
||||
return of(String.class, o -> o instanceof String ? (String) o : o.toString());
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Boolean> ofBoolean() {
|
||||
return of(Boolean.class, data -> {
|
||||
if (data instanceof Boolean) return (Boolean) data;
|
||||
String v = data.toString().trim();
|
||||
if (Arrays.stream(TRUE_VALUES).anyMatch(v::equalsIgnoreCase)) return true;
|
||||
else if (Arrays.stream(FALSE_VALUES).anyMatch(v::equalsIgnoreCase)) return false;
|
||||
else throw new IllegalArgumentException("Cannot parse boolean from " + data);
|
||||
});
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Boolean> ofBooleanType() {
|
||||
return of(boolean.class, o -> o instanceof Boolean ? (Boolean) o : Boolean.parseBoolean(o.toString()));
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Character> ofCharacter() {
|
||||
return of(Character.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0));
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Character> ofCharacterType() {
|
||||
return of(char.class, o -> o instanceof Character ? (Character) o : o.toString().charAt(0));
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Integer> ofInteger() {
|
||||
return ofNumber(Integer.class, Number::intValue, Integer::parseInt);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Integer> ofIntegerType() {
|
||||
return ofNumber(int.class, Number::intValue, Integer::parseInt);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Long> ofLong() {
|
||||
return ofNumber(Long.class, Number::longValue, Long::parseLong);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Long> ofLongType() {
|
||||
return ofNumber(long.class, Number::longValue, Long::parseLong);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Double> ofDouble() {
|
||||
return ofNumber(Double.class, Number::doubleValue, Double::parseDouble);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Double> ofDoubleType() {
|
||||
return ofNumber(double.class, Number::doubleValue, Double::parseDouble);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Float> ofFloat() {
|
||||
return ofNumber(Float.class, Number::floatValue, Float::parseFloat);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Float> ofFloatType() {
|
||||
return ofNumber(float.class, Number::floatValue, Float::parseFloat);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Short> ofShort() {
|
||||
return ofNumber(Short.class, Number::shortValue, Short::parseShort);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Short> ofShortType() {
|
||||
return ofNumber(short.class, Number::shortValue, Short::parseShort);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Byte> ofByte() {
|
||||
return ofNumber(Byte.class, Number::byteValue, Byte::parseByte);
|
||||
}
|
||||
|
||||
public static PrimitiveAdapter<Byte> ofByteType() {
|
||||
return ofNumber(byte.class, Number::byteValue, Byte::parseByte);
|
||||
}
|
||||
|
||||
public static <T> PrimitiveAdapter<T> of(@NotNull Class<T> clazz,
|
||||
@NotNull DataFunction<Object, T> function) {
|
||||
return new PrimitiveAdapter<>(clazz, (p, type, data) -> function.handle(data));
|
||||
}
|
||||
|
||||
public static <T extends Number> PrimitiveAdapter<T> ofNumber(@NotNull Class<T> numberClass,
|
||||
@NotNull DataFunction<Number, T> castFunction,
|
||||
@NotNull DataFunction<String, T> parseFunction) {
|
||||
return of(numberClass, o -> o instanceof Number ? castFunction.handle((Number) o) : parseFunction.handle(o.toString()));
|
||||
}
|
||||
|
||||
protected PrimitiveAdapter(@NotNull Class<T> valueType, @NotNull ValueParser<T> deserializer) {
|
||||
super(ValueType.of(valueType), (provider, type, value) -> value, deserializer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cc.carm.lib.configuration.adapter.strandard;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter.*;
|
||||
|
||||
public interface StandardAdapters {
|
||||
|
||||
@NotNull PrimitiveAdapter<?>[] PRIMITIVES = new PrimitiveAdapter[]{
|
||||
ofString(), ofBoolean(), ofBooleanType(), ofCharacter(), ofCharacterType(),
|
||||
ofInteger(), ofIntegerType(), ofLong(), ofLongType(), ofDouble(), ofDoubleType(),
|
||||
ofFloat(), ofFloatType(), ofShort(), ofShortType(), ofByte(), ofByteType()
|
||||
};
|
||||
|
||||
@NotNull ValueAdapter<Enum<?>> ENUMS = PrimitiveAdapter.ofEnum();
|
||||
|
||||
@NotNull ValueAdapter<ConfigureSection> SECTIONS = new ValueAdapter<>(
|
||||
ValueType.of(ConfigureSection.class),
|
||||
(provider, type, value) -> value,
|
||||
(provider, type, value) -> {
|
||||
if (value instanceof ConfigureSection) {
|
||||
return (ConfigureSection) value;
|
||||
} else throw new IllegalArgumentException("Value is not a ConfigurationSection");
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cc.carm.lib.configuration.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* The configuration path used to mark the corresponding class or parameter.
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConfigPath {
|
||||
|
||||
/**
|
||||
* The path value of the current configuration.
|
||||
* If not set,will generate the path by {@link cc.carm.lib.configuration.source.loader.PathGenerator}.
|
||||
*
|
||||
* @return The path value of the current configuration
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Whether to start with the root node.
|
||||
* <br>If false, the previous path (if any) to the node is automatically added.
|
||||
* <br>If true, the path will be set directly from the root node.
|
||||
*
|
||||
* @return Whether to start directly from the root path without inheriting the previous path
|
||||
*/
|
||||
boolean root() default false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package cc.carm.lib.configuration.builder;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class AbstractConfigBuilder<
|
||||
TYPE, RESULT extends ConfigValue<TYPE>, HOLDER extends ConfigurationHolder<?>,
|
||||
SELF extends AbstractConfigBuilder<TYPE, RESULT, HOLDER, SELF>
|
||||
> {
|
||||
|
||||
protected final Class<? super HOLDER> providerClass;
|
||||
protected final ValueType<TYPE> type;
|
||||
|
||||
protected @Nullable HOLDER holder;
|
||||
protected @Nullable String path;
|
||||
|
||||
protected @NotNull Supplier<@Nullable TYPE> defaultValueSupplier = () -> null;
|
||||
protected @NotNull BiConsumer<ConfigurationHolder<?>, String> initializer = (h, p) -> {
|
||||
};
|
||||
|
||||
protected AbstractConfigBuilder(Class<? super HOLDER> providerClass, ValueType<TYPE> type) {
|
||||
this.providerClass = providerClass;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<TYPE> type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
protected abstract @NotNull SELF self();
|
||||
|
||||
public abstract @NotNull RESULT build();
|
||||
|
||||
public @NotNull SELF holder(@Nullable HOLDER holder) {
|
||||
this.holder = holder;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF path(@Nullable String path) {
|
||||
this.path = path;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF initializer(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
|
||||
this.initializer = initializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF append(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
|
||||
return initializer(initializer.andThen(initializer));
|
||||
}
|
||||
|
||||
public @NotNull SELF append(@NotNull Consumer<ConfigurationHolder<?>> initializer) {
|
||||
return append((provider, valuePath) -> initializer.accept(provider));
|
||||
}
|
||||
|
||||
public @NotNull SELF defaults(@Nullable TYPE defaultValue) {
|
||||
return defaults(() -> defaultValue);
|
||||
}
|
||||
|
||||
public @NotNull SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) {
|
||||
this.defaultValueSupplier = supplier;
|
||||
return self();
|
||||
}
|
||||
|
||||
public <M> @NotNull SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) {
|
||||
return append((h, p) -> metaConsumer.accept(h.metadata(p)));
|
||||
}
|
||||
|
||||
public <M> @NotNull SELF meta(@NotNull ConfigurationMetadata<M> type, @Nullable M value) {
|
||||
return meta(h -> h.set(type, value));
|
||||
}
|
||||
|
||||
protected @NotNull ValueManifest<TYPE> buildManifest() {
|
||||
return new ValueManifest<>(
|
||||
type(), this.defaultValueSupplier, this.initializer,
|
||||
this.holder, this.path
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cc.carm.lib.configuration.builder;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
|
||||
public abstract class CommonConfigBuilder<TYPE, RESULT extends ConfigValue<TYPE>, SELF extends CommonConfigBuilder<TYPE, RESULT, SELF>>
|
||||
extends AbstractConfigBuilder<TYPE, RESULT, ConfigurationHolder<?>, SELF> {
|
||||
|
||||
protected CommonConfigBuilder(ValueType<TYPE> type) {
|
||||
super(ConfigurationHolder.class, type);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package cc.carm.lib.configuration.builder.impl;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
|
||||
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.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractSectionBuilder<
|
||||
TYPE, PARAM,
|
||||
RESULT extends ConfigValue<TYPE>,
|
||||
SELF extends AbstractSectionBuilder<TYPE, PARAM, RESULT, SELF>
|
||||
> extends CommonConfigBuilder<TYPE, RESULT, SELF> {
|
||||
|
||||
|
||||
protected final @NotNull ValueType<PARAM> paramType;
|
||||
|
||||
protected @NotNull ValueHandler<ConfigureSection, PARAM> parser;
|
||||
protected @NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer;
|
||||
|
||||
protected AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<PARAM> paramType,
|
||||
@NotNull ValueHandler<ConfigureSection, PARAM> parser,
|
||||
@NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
|
||||
super(type);
|
||||
this.paramType = paramType;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(@NotNull DataFunction<ConfigureSection, PARAM> valueParser) {
|
||||
return parse((p, section) -> valueParser.handle(section));
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(@NotNull ValueHandler<ConfigureSection, PARAM> valueParser) {
|
||||
this.parser = valueParser;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(@NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
|
||||
this.serializer = serializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(@NotNull DataFunction<PARAM, ? extends Map<String, Object>> serializer) {
|
||||
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<>();
|
||||
serializer.accept(h, map, value);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
protected ValueAdapter<PARAM> buildAdapter() {
|
||||
return new ValueAdapter<>(this.paramType)
|
||||
.parser((p, type, data) -> {
|
||||
ConfigureSection section = p.deserialize(ConfigureSection.class, data);
|
||||
if (section == null) return null;
|
||||
return this.parser.handle(p, section);
|
||||
})
|
||||
.serializer((p, type, data) -> {
|
||||
Map<String, Object> map = this.serializer.handle(p, data);
|
||||
return map == null || map.isEmpty() ? null : map;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package cc.carm.lib.configuration.builder.impl;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class AbstractSourceBuilder<
|
||||
V, SOURCE, PARAM, RESULT extends ConfigValue<V>,
|
||||
SELF extends AbstractSourceBuilder<V, SOURCE, PARAM, RESULT, SELF>
|
||||
> extends CommonConfigBuilder<V, RESULT, SELF> {
|
||||
|
||||
protected final @NotNull ValueType<SOURCE> sourceType;
|
||||
protected final @NotNull ValueType<PARAM> paramType;
|
||||
protected @NotNull ValueHandler<SOURCE, PARAM> valueParser;
|
||||
protected @NotNull ValueHandler<PARAM, SOURCE> valueSerializer;
|
||||
|
||||
protected AbstractSourceBuilder(@NotNull ValueType<V> type,
|
||||
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<PARAM> paramType,
|
||||
@NotNull ValueHandler<SOURCE, PARAM> parser,
|
||||
@NotNull ValueHandler<PARAM, SOURCE> serializer) {
|
||||
super(type);
|
||||
this.sourceType = sourceType;
|
||||
this.paramType = paramType;
|
||||
this.valueParser = parser;
|
||||
this.valueSerializer = serializer;
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(@NotNull DataFunction<SOURCE, PARAM> parser) {
|
||||
return parse((p, source) -> parser.handle(source));
|
||||
}
|
||||
|
||||
public @NotNull SELF parse(@NotNull ValueHandler<SOURCE, PARAM> parser) {
|
||||
this.valueParser = parser;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(@NotNull ValueHandler<PARAM, SOURCE> serializer) {
|
||||
this.valueSerializer = serializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
public @NotNull SELF serialize(@NotNull DataFunction<PARAM, SOURCE> serializer) {
|
||||
return serialize((p, value) -> serializer.handle(value));
|
||||
}
|
||||
|
||||
protected ValueAdapter<PARAM> buildAdapter() {
|
||||
return new ValueAdapter<>(this.paramType)
|
||||
.parser((holder, type, data) -> {
|
||||
SOURCE source = holder.deserialize(this.sourceType, data);
|
||||
return this.valueParser.handle(holder, source);
|
||||
})
|
||||
.serializer((holder, type, data) -> {
|
||||
SOURCE source = this.valueSerializer.handle(holder, data);
|
||||
return holder.serialize(source);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package cc.carm.lib.configuration.builder.list;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ConfigListBuilder<V> {
|
||||
|
||||
protected final @NotNull ValueType<V> type;
|
||||
|
||||
public ConfigListBuilder(@NotNull ValueType<V> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public <S> @NotNull SourceListBuilder<S, V> from(@NotNull Class<S> sourceType) {
|
||||
return from(ValueType.of(sourceType));
|
||||
}
|
||||
|
||||
public <S> @NotNull SourceListBuilder<S, V> from(@NotNull ValueType<S> sourceType) {
|
||||
return new SourceListBuilder<>(
|
||||
ArrayList::new, sourceType, type,
|
||||
ValueHandler.required(type),
|
||||
ValueHandler.required(sourceType)
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<String, V> fromString() {
|
||||
return new SourceListBuilder<>(
|
||||
ArrayList::new, ValueType.STRING, type,
|
||||
ValueHandler.required(type), ValueHandler.stringValue()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SectionListBuilder<V> fromSection() {
|
||||
return new SectionListBuilder<>(
|
||||
ArrayList::new, type,
|
||||
ValueHandler.required(type), ValueHandler.required()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package cc.carm.lib.configuration.builder.list;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SectionListBuilder<V> extends AbstractSectionBuilder<List<V>, V, ConfiguredList<V>, SectionListBuilder<V>> {
|
||||
|
||||
protected @NotNull Supplier<? extends List<V>> constructor;
|
||||
|
||||
public SectionListBuilder(@NotNull Supplier<? extends List<V>> constructor,
|
||||
@NotNull ValueType<V> paramType,
|
||||
@NotNull ValueHandler<ConfigureSection, V> parser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer) {
|
||||
super(new ValueType<List<V>>() {
|
||||
}, paramType, parser, serializer);
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final @NotNull SectionListBuilder<V> defaults(@NotNull V... values) {
|
||||
return defaults(new ArrayList<>(Arrays.asList(values)));
|
||||
}
|
||||
|
||||
public final @NotNull SectionListBuilder<V> defaults(@NotNull Collection<V> values) {
|
||||
return defaults(new ArrayList<>(values));
|
||||
}
|
||||
|
||||
public SectionListBuilder<V> constructor(@NotNull Supplier<? extends List<V>> constructor) {
|
||||
this.constructor = constructor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public <LIST extends List<V>> SectionListBuilder<V> construct(@NotNull LIST list) {
|
||||
return constructor(() -> list);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SectionListBuilder<V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredList<V> build() {
|
||||
return new ConfiguredList<>(buildManifest(), constructor, buildAdapter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package cc.carm.lib.configuration.builder.list;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SourceListBuilder<SOURCE, V>
|
||||
extends AbstractSourceBuilder<List<V>, SOURCE, V, ConfiguredList<V>, SourceListBuilder<SOURCE, V>> {
|
||||
|
||||
protected @NotNull Supplier<? extends List<V>> constructor;
|
||||
|
||||
public SourceListBuilder(@NotNull Supplier<? extends List<V>> constructor,
|
||||
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<V> paramType,
|
||||
@NotNull ValueHandler<SOURCE, V> parser, @NotNull ValueHandler<V, SOURCE> serializer) {
|
||||
super(new ValueType<List<V>>() {
|
||||
}, sourceType, paramType, parser, serializer);
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final @NotNull SourceListBuilder<SOURCE, V> defaults(@NotNull V... values) {
|
||||
return defaults(new ArrayList<>(Arrays.asList(values)));
|
||||
}
|
||||
|
||||
public final @NotNull SourceListBuilder<SOURCE, V> defaults(@NotNull Collection<V> values) {
|
||||
return defaults(new ArrayList<>(values));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceListBuilder<SOURCE, V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredList<V> build() {
|
||||
return new ConfiguredList<>(buildManifest(), this.constructor, buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package cc.carm.lib.configuration.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
|
||||
|
||||
|
||||
protected final @NotNull Supplier<@NotNull M> constructor;
|
||||
protected final @NotNull ValueType<K> keyType;
|
||||
protected final @NotNull ValueType<V> valueType;
|
||||
|
||||
public ConfigMapBuilder(@NotNull Supplier<@NotNull M> constructor,
|
||||
@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
|
||||
this.constructor = constructor;
|
||||
this.keyType = keyType;
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
public <W extends Map<K, V>> ConfigMapBuilder<W, K, V> constructor(@NotNull Supplier<W> supplier) {
|
||||
return new ConfigMapBuilder<>(supplier, keyType, valueType);
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> clazz) {
|
||||
return from(ValueType.of(clazz));
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceMapBuilder<M, S, K, V> from(@NotNull ValueType<S> sourceType) {
|
||||
return from(
|
||||
sourceType,
|
||||
ValueHandler.required(keyType), ValueHandler.stringValue(),
|
||||
ValueHandler.required(valueType), ValueHandler.required()
|
||||
);
|
||||
}
|
||||
|
||||
public <S> @NotNull SourceMapBuilder<M, S, K, V> from(@NotNull ValueType<S> sourceType,
|
||||
@NotNull ValueHandler<String, K> keyParser,
|
||||
@NotNull ValueHandler<K, String> keySerializer,
|
||||
@NotNull ValueHandler<S, V> valueParser,
|
||||
@NotNull ValueHandler<V, S> valueSerializer) {
|
||||
return new SourceMapBuilder<>(
|
||||
this.constructor, sourceType, keyType, valueType,
|
||||
keyParser, keySerializer, valueParser, valueSerializer
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, String, K, V> fromString() {
|
||||
return from(
|
||||
ValueType.STRING,
|
||||
ValueHandler.required(keyType), ValueHandler.stringValue(),
|
||||
ValueHandler.required(valueType), ValueHandler.stringValue()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<M, K, V> fromSection() {
|
||||
return fromSection(
|
||||
ValueHandler.required(keyType), ValueHandler.stringValue(),
|
||||
ValueHandler.required(valueType), ValueHandler.required()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<M, K, V> fromSection(
|
||||
@NotNull ValueHandler<String, K> keyParser,
|
||||
@NotNull ValueHandler<K, String> keySerializer,
|
||||
@NotNull ValueHandler<ConfigureSection, V> valueParser,
|
||||
@NotNull ValueHandler<V, Map<String, Object>> valueSerializer
|
||||
) {
|
||||
return new SectionMapBuilder<>(
|
||||
this.constructor, keyType, valueType,
|
||||
keyParser, keySerializer, valueParser, valueSerializer
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cc.carm.lib.configuration.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigMapCreator<K, V> {
|
||||
|
||||
protected final @NotNull ValueType<K> keyType;
|
||||
protected final @NotNull ValueType<V> valueType;
|
||||
|
||||
public ConfigMapCreator(@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
|
||||
this.keyType = keyType;
|
||||
this.valueType = valueType;
|
||||
}
|
||||
|
||||
public <M extends Map<K, V>> @NotNull ConfigMapBuilder<M, K, V> constructor(@NotNull Supplier<@NotNull M> mapSuppler) {
|
||||
return new ConfigMapBuilder<>(mapSuppler, keyType, valueType);
|
||||
}
|
||||
|
||||
public <W extends Map<K, V>> @NotNull ConfigMapBuilder<W, K, V> constructor(@NotNull Class<W> type) {
|
||||
return constructor(() -> {
|
||||
try {
|
||||
return type.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<HashMap<K, V>, K, V> asHashMap() {
|
||||
return constructor(HashMap::new);
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asLinkedMap() {
|
||||
return constructor(LinkedHashMap::new);
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap() {
|
||||
return constructor(TreeMap::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package cc.carm.lib.configuration.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SectionMapBuilder<MAP extends Map<K, V>, K, V>
|
||||
extends AbstractSectionBuilder<
|
||||
Map<K, V>, V, ConfiguredMap<K, V>,
|
||||
SectionMapBuilder<MAP, K, V>
|
||||
> {
|
||||
|
||||
protected final @NotNull ValueType<K> keyType;
|
||||
|
||||
protected @NotNull Supplier<? extends MAP> constructor;
|
||||
protected @NotNull ValueHandler<String, K> keyParser;
|
||||
protected @NotNull ValueHandler<K, String> keySerializer;
|
||||
|
||||
public SectionMapBuilder(@NotNull Supplier<? extends MAP> constructor,
|
||||
@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType,
|
||||
@NotNull ValueHandler<String, K> keyParser,
|
||||
@NotNull ValueHandler<K, String> keySerializer,
|
||||
@NotNull ValueHandler<ConfigureSection, V> valueParser,
|
||||
@NotNull ValueHandler<V, Map<String, Object>> valueSerializer) {
|
||||
super(new ValueType<Map<K, V>>() {
|
||||
}, valueType, valueParser, valueSerializer);
|
||||
this.keyType = keyType;
|
||||
this.constructor = constructor;
|
||||
this.keyParser = keyParser;
|
||||
this.keySerializer = keySerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SectionMapBuilder<MAP, K, V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> parseKey(@NotNull DataFunction<String, K> keyParser) {
|
||||
return parseKey((holder, data) -> keyParser.handle(data));
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> parseKey(@NotNull ValueHandler<String, K> keyParser) {
|
||||
this.keyParser = keyParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> serializeKey(@NotNull DataFunction<K, String> keySerializer) {
|
||||
return serializeKey((holder, data) -> keySerializer.handle(data));
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> serializeKey(@NotNull ValueHandler<K, String> keySerializer) {
|
||||
this.keySerializer = keySerializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> defaults(@NotNull MAP defaults) {
|
||||
return defaults(() -> defaults);
|
||||
}
|
||||
|
||||
public @NotNull SectionMapBuilder<MAP, K, V> defaults(@NotNull Consumer<MAP> defaults) {
|
||||
return defaults(() -> {
|
||||
MAP map = this.constructor.get();
|
||||
defaults.accept(map);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
public @NotNull ValueAdapter<K> buildKeyAdapter() {
|
||||
return new ValueAdapter<>(this.keyType)
|
||||
.parser((holder, type, data) -> {
|
||||
String source = holder.deserialize(String.class, data);
|
||||
return this.keyParser.handle(holder, source);
|
||||
})
|
||||
.serializer((holder, type, data) -> {
|
||||
String source = this.keySerializer.handle(holder, data);
|
||||
return holder.serialize(source);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredMap<K, V> build() {
|
||||
return new ConfiguredMap<>(buildManifest(), this.constructor, buildKeyAdapter(), this.buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package cc.carm.lib.configuration.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SourceMapBuilder<MAP extends Map<K, V>, SOURCE, K, V>
|
||||
extends AbstractSourceBuilder<
|
||||
Map<K, V>, SOURCE, V, ConfiguredMap<K, V>,
|
||||
SourceMapBuilder<MAP, SOURCE, K, V>
|
||||
> {
|
||||
|
||||
protected final @NotNull ValueType<K> keyType;
|
||||
|
||||
protected @NotNull Supplier<? extends MAP> constructor;
|
||||
protected @NotNull ValueHandler<String, K> keyParser;
|
||||
protected @NotNull ValueHandler<K, String> keySerializer;
|
||||
|
||||
public SourceMapBuilder(@NotNull Supplier<? extends MAP> constructor, @NotNull ValueType<SOURCE> sourceType,
|
||||
@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType,
|
||||
@NotNull ValueHandler<String, K> keyParser, @NotNull ValueHandler<K, String> keySerializer,
|
||||
@NotNull ValueHandler<SOURCE, V> valueParser, @NotNull ValueHandler<V, SOURCE> valueSerializer) {
|
||||
super(new ValueType<Map<K, V>>() {
|
||||
}, sourceType, valueType, valueParser, valueSerializer);
|
||||
this.keyType = keyType;
|
||||
this.constructor = constructor;
|
||||
this.keyParser = keyParser;
|
||||
this.keySerializer = keySerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceMapBuilder<MAP, SOURCE, K, V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> defaults(@NotNull MAP defaults) {
|
||||
return defaults(() -> defaults);
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> defaults(@NotNull Consumer<MAP> defaults) {
|
||||
return defaults(() -> {
|
||||
MAP map = this.constructor.get();
|
||||
defaults.accept(map);
|
||||
return map;
|
||||
});
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> parseKey(@NotNull DataFunction<String, K> keyParser) {
|
||||
return parseKey((holder, data) -> keyParser.handle(data));
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> parseKey(@NotNull ValueHandler<String, K> keyParser) {
|
||||
this.keyParser = keyParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> serializeKey(@NotNull DataFunction<K, String> keySerializer) {
|
||||
return serializeKey((holder, data) -> keySerializer.handle(data));
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> serializeKey(@NotNull ValueHandler<K, String> keySerializer) {
|
||||
this.keySerializer = keySerializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull ValueAdapter<K> buildKeyAdapter() {
|
||||
return new ValueAdapter<>(this.keyType)
|
||||
.parser((holder, type, data) -> {
|
||||
String source = holder.deserialize(String.class, data);
|
||||
return this.keyParser.handle(holder, source);
|
||||
})
|
||||
.serializer((holder, type, data) -> {
|
||||
String source = this.keySerializer.handle(holder, data);
|
||||
return holder.serialize(source);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredMap<K, V> build() {
|
||||
return new ConfiguredMap<>(buildManifest(), this.constructor, buildKeyAdapter(), this.buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package cc.carm.lib.configuration.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigValueBuilder<V> {
|
||||
|
||||
protected final @NotNull ValueType<V> type;
|
||||
|
||||
public ConfigValueBuilder(@NotNull ValueType<V> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull Class<S> clazz) {
|
||||
return from(ValueType.of(clazz));
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull ValueType<S> sourceType) {
|
||||
return from(sourceType, ValueHandler.required(type), ValueHandler.required(sourceType));
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull ValueType<S> sourceType,
|
||||
@NotNull ValueHandler<S, V> valueParser,
|
||||
@NotNull ValueHandler<V, S> valueSerializer) {
|
||||
return new SourceValueBuilder<>(sourceType, this.type, valueParser, valueSerializer);
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<String, V> fromString() {
|
||||
return from(ValueType.STRING, ValueHandler.required(type), ValueHandler.stringValue());
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection() {
|
||||
return fromSection(ValueHandler.required(type), ValueHandler.required());
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection(
|
||||
@NotNull ValueHandler<ConfigureSection, V> valueParser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, Object>> valueSerializer
|
||||
) {
|
||||
return new SectionValueBuilder<>(this.type, valueParser, valueSerializer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cc.carm.lib.configuration.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SectionValueBuilder<V> extends AbstractSectionBuilder<V, V, ConfiguredValue<V>, SectionValueBuilder<V>> {
|
||||
|
||||
public SectionValueBuilder(@NotNull ValueType<V> type,
|
||||
@NotNull ValueHandler<ConfigureSection, V> parser,
|
||||
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer) {
|
||||
super(type, type, parser, serializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SectionValueBuilder<V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredValue<V> build() {
|
||||
return ConfiguredValue.of(buildManifest(), buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package cc.carm.lib.configuration.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
|
||||
import cc.carm.lib.configuration.function.ValueHandler;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SourceValueBuilder<S, V> extends AbstractSourceBuilder<V, S, V, ConfiguredValue<V>, SourceValueBuilder<S, V>> {
|
||||
|
||||
|
||||
public SourceValueBuilder(@NotNull ValueType<S> sourceType, @NotNull ValueType<V> valueType,
|
||||
@NotNull ValueHandler<S, V> parser, @NotNull ValueHandler<V, S> serializer) {
|
||||
super(valueType, sourceType, valueType, parser, serializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceValueBuilder<S, V> self() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredValue<V> build() {
|
||||
return new ConfiguredValue<>(buildManifest(), buildAdapter());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
package cc.carm.lib.configuration.core;
|
||||
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.annotation.InlineComment;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 配置文件类初始化方法
|
||||
* 用于初始化 {@link ConfigurationRoot} 中的每个 {@link ConfigValue} 对象
|
||||
*
|
||||
* @param <T> {@link ConfigurationProvider} 配置文件的数据来源
|
||||
* @author CarmJos
|
||||
*/
|
||||
public class ConfigInitializer<T extends ConfigurationProvider<?>> {
|
||||
|
||||
protected final @NotNull T provider;
|
||||
|
||||
public ConfigInitializer(@NotNull T provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
||||
*
|
||||
* @param clazz 配置文件类,须继承于 {@link ConfigurationRoot} 。
|
||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
||||
*/
|
||||
public void initialize(@NotNull Class<? extends ConfigurationRoot> clazz, boolean saveDefaults) {
|
||||
initialize(clazz, saveDefaults, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化指定类的所有 {@link ConfigValue} 对象。
|
||||
*
|
||||
* @param clazz 配置文件类,须继承于 {@link ConfigurationRoot} 。
|
||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
||||
* @param loadSubClasses 是否加载内部子类(默认为 true)。
|
||||
*/
|
||||
public void initialize(@NotNull Class<? extends ConfigurationRoot> clazz,
|
||||
boolean saveDefaults, boolean loadSubClasses) {
|
||||
initializeStaticClass(
|
||||
clazz, null, null,
|
||||
null, null, null,
|
||||
saveDefaults, loadSubClasses
|
||||
);
|
||||
if (saveDefaults) {
|
||||
try {
|
||||
provider.save();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。
|
||||
*
|
||||
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。
|
||||
*/
|
||||
public void initialize(@NotNull ConfigurationRoot config) {
|
||||
initialize(config, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。
|
||||
*
|
||||
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。
|
||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
||||
*/
|
||||
public void initialize(@NotNull ConfigurationRoot config, boolean saveDefaults) {
|
||||
initializeInstance(
|
||||
config, null, null,
|
||||
null, null, null,
|
||||
saveDefaults
|
||||
);
|
||||
if (saveDefaults) {
|
||||
try {
|
||||
provider.save();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 针对实例类的初始化方法
|
||||
private void initializeInstance(@NotNull ConfigurationRoot root,
|
||||
@Nullable String parentPath, @Nullable String fieldName,
|
||||
@Nullable ConfigPath fieldPath,
|
||||
@Nullable HeaderComment fieldHeaderComments,
|
||||
@Nullable InlineComment fieldInlineComments,
|
||||
boolean saveDefaults) {
|
||||
String path = getClassPath(root.getClass(), parentPath, fieldName, fieldPath);
|
||||
this.provider.setHeaderComment(path, getClassHeaderComments(root.getClass(), fieldHeaderComments));
|
||||
if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments));
|
||||
|
||||
for (Field field : root.getClass().getDeclaredFields()) {
|
||||
initializeField(root, field, path, saveDefaults, false);
|
||||
}
|
||||
}
|
||||
|
||||
// 针对静态类的初始化方法
|
||||
private void initializeStaticClass(@NotNull Class<?> clazz,
|
||||
@Nullable String parentPath, @Nullable String fieldName,
|
||||
@Nullable ConfigPath fieldPath,
|
||||
@Nullable HeaderComment fieldHeaderComments,
|
||||
@Nullable InlineComment fieldInlineComments,
|
||||
boolean saveDefaults, boolean loadSubClasses) {
|
||||
if (!ConfigurationRoot.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类
|
||||
String path = getClassPath(clazz, parentPath, fieldName, fieldPath);
|
||||
this.provider.setHeaderComment(path, getClassHeaderComments(clazz, fieldHeaderComments));
|
||||
if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments));
|
||||
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
initializeField(clazz, field, path, saveDefaults, loadSubClasses);
|
||||
}
|
||||
|
||||
if (!loadSubClasses) return;
|
||||
Class<?>[] classes = clazz.getDeclaredClasses();
|
||||
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
|
||||
initializeStaticClass(
|
||||
classes[i], path, classes[i].getSimpleName(),
|
||||
null, null, null,
|
||||
saveDefaults, true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeField(@NotNull Object source, @NotNull Field field,
|
||||
@Nullable String parent, boolean saveDefaults, boolean loadSubClasses) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
Object object = field.get(source);
|
||||
|
||||
if (object instanceof ConfigValue<?>) {
|
||||
initializeValue(
|
||||
(ConfigValue<?>) object, getFieldPath(field, parent),
|
||||
field.getAnnotation(HeaderComment.class),
|
||||
field.getAnnotation(InlineComment.class),
|
||||
saveDefaults
|
||||
);
|
||||
} else if (source instanceof ConfigurationRoot && object instanceof ConfigurationRoot) {
|
||||
// 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。
|
||||
initializeInstance(
|
||||
(ConfigurationRoot) object, parent, field.getName(),
|
||||
field.getAnnotation(ConfigPath.class),
|
||||
field.getAnnotation(HeaderComment.class),
|
||||
field.getAnnotation(InlineComment.class),
|
||||
saveDefaults
|
||||
);
|
||||
} else if (source instanceof Class<?> && object instanceof Class<?>) {
|
||||
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
|
||||
initializeStaticClass(
|
||||
(Class<?>) object, parent, field.getName(),
|
||||
field.getAnnotation(ConfigPath.class),
|
||||
field.getAnnotation(HeaderComment.class),
|
||||
field.getAnnotation(InlineComment.class),
|
||||
saveDefaults, loadSubClasses
|
||||
);
|
||||
}
|
||||
|
||||
// 以上判断实现以下规范:
|
||||
// - 实例类中仅加载 ConfigValue实例 与 ConfigurationRoot实例
|
||||
// - 静态类中仅加载 静态ConfigValue实例 与 静态ConfigurationRoot类
|
||||
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
protected void initializeValue(@NotNull ConfigValue<?> value, @NotNull String path,
|
||||
@Nullable HeaderComment fieldHeaderComment,
|
||||
@Nullable InlineComment fieldInlineComment,
|
||||
boolean saveDefaults) {
|
||||
value.initialize(
|
||||
provider, saveDefaults, path,
|
||||
readHeaderComments(fieldHeaderComment),
|
||||
readInlineComments(fieldInlineComment)
|
||||
);
|
||||
}
|
||||
|
||||
protected static @Nullable List<String> getClassHeaderComments(@NotNull Class<?> clazz,
|
||||
@Nullable HeaderComment fieldAnnotation) {
|
||||
List<String> classComments = readHeaderComments(clazz.getAnnotation(HeaderComment.class));
|
||||
if (classComments != null) return classComments;
|
||||
else return readHeaderComments(fieldAnnotation);
|
||||
}
|
||||
|
||||
|
||||
protected static List<String> readHeaderComments(@Nullable HeaderComment annotation) {
|
||||
if (annotation == null) return null;
|
||||
String[] value = annotation.value();
|
||||
return value.length > 0 ? Arrays.asList(value) : null;
|
||||
}
|
||||
|
||||
|
||||
protected static @Nullable String readInlineComments(@Nullable InlineComment annotation) {
|
||||
if (annotation == null) return null;
|
||||
String value = annotation.value();
|
||||
return value.length() > 0 ? value : null;
|
||||
}
|
||||
|
||||
protected static @Nullable String getClassPath(@Nullable Class<?> clazz,
|
||||
@Nullable String parentPath,
|
||||
@Nullable String filedName,
|
||||
@Nullable ConfigPath fieldAnnotation) {
|
||||
@NotNull String parent = parentPath != null ? parentPath + "." : "";
|
||||
boolean fromRoot = false;
|
||||
|
||||
// 先获取 Class 对应的路径注解 其优先度最高。
|
||||
if (clazz != null) {
|
||||
ConfigPath clazzAnnotation = clazz.getAnnotation(ConfigPath.class);
|
||||
if (clazzAnnotation != null) {
|
||||
fromRoot = clazzAnnotation.root();
|
||||
if (clazzAnnotation.value().length() > 0) {
|
||||
return (fromRoot ? "" : parent) + clazzAnnotation.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldAnnotation != null) {
|
||||
fromRoot = fromRoot || fieldAnnotation.root();
|
||||
if (fieldAnnotation.value().length() > 0) {
|
||||
return (fromRoot ? "" : parent) + fieldAnnotation.value();
|
||||
}
|
||||
}
|
||||
|
||||
// 再由 fieldName 获取路径
|
||||
if (filedName != null) return (fromRoot ? "" : parent) + getPathFromName(filedName);
|
||||
else return null; // 不满足上述条件 且 无 fieldName,则说明是根路径。
|
||||
}
|
||||
|
||||
protected static @NotNull String getFieldPath(@NotNull Field field, @Nullable String parentPath) {
|
||||
@NotNull String parent = parentPath != null ? parentPath + "." : "";
|
||||
boolean fromRoot = false;
|
||||
|
||||
// 先获取 Field 对应的路径注解 其优先度最高。
|
||||
ConfigPath pathAnnotation = field.getAnnotation(ConfigPath.class);
|
||||
if (pathAnnotation != null) {
|
||||
fromRoot = pathAnnotation.root();
|
||||
if (pathAnnotation.value().length() > 0) {
|
||||
return (fromRoot ? "" : parent) + pathAnnotation.value();
|
||||
}
|
||||
}
|
||||
|
||||
// 最后再通过 fieldName 自动生成路径
|
||||
return (fromRoot ? "" : parent) + getPathFromName(field.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到指定元素的配置名称。
|
||||
* 采用 全小写,以“-”链接 的命名规则。
|
||||
*
|
||||
* @param name 源名称
|
||||
* @return 全小写,以“-”链接 的 路径名称
|
||||
*/
|
||||
public static String getPathFromName(String name) {
|
||||
return name.replaceAll("[A-Z]", "-$0") // 将驼峰转换为蛇形;
|
||||
.replaceAll("-(.*)", "$1") // 若首字母也为大写,则也会被转换,需要去掉第一个横线
|
||||
.replaceAll("_-([A-Z])", "_$1") // 因为命名中可能包含 _,因此需要被特殊处理一下
|
||||
.replaceAll("([a-z])-([A-Z])", "$1_$2") // 然后将非全大写命名的内容进行转换
|
||||
.replace("-", "") // 移除掉多余的横线
|
||||
.replace("_", "-") // 将下划线替换为横线
|
||||
.toLowerCase(); // 最后转为全小写
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package cc.carm.lib.configuration.core;
|
||||
|
||||
/**
|
||||
* 配置文件类的根节点,用于标注该类用于记录配置文件中的配置信息。
|
||||
*/
|
||||
public abstract class ConfigurationRoot {
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.annotation;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 用于标记对应类或参数的配置路径
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConfigPath {
|
||||
|
||||
/**
|
||||
* 指定路径的值。
|
||||
* 若不指定,则会通过 {@link ConfigInitializer#getPathFromName(String)} 自动生成当前路径的值。
|
||||
*
|
||||
* @return 路径的值
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* 是否从根节点开始。
|
||||
* <br>若为 false,则会自动添加上一个路径(如果有)到本节点的路径。
|
||||
* <br>若为 true,则会从根节点开始直接设置本路径。
|
||||
*
|
||||
* @return 是否不继承上一路径,直接从根路径为开始
|
||||
*/
|
||||
boolean root() default false;
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.annotation;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* 行内注释,用于给对应配置的所在行添加注释,便于使用者阅读查看。
|
||||
* 如:
|
||||
* <blockquote><pre>
|
||||
* foo: "bar" # 注释内容
|
||||
* </pre></blockquote>
|
||||
*/
|
||||
@Target({ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface InlineComment {
|
||||
|
||||
/**
|
||||
* 注释内容,若内容长度为0则不会添加注释
|
||||
* <p> 如 <b>"foobar"</b> 将被设定为
|
||||
* <blockquote><pre>
|
||||
* foo: "bar" # foobar
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @return 注释内容
|
||||
*/
|
||||
@NotNull
|
||||
String value() default "";
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class AbstractConfigBuilder<T, B extends AbstractConfigBuilder<T, B, P>, P extends ConfigurationProvider<?>> {
|
||||
|
||||
protected final Class<? super P> providerClass;
|
||||
|
||||
protected @Nullable P provider;
|
||||
protected @Nullable String path;
|
||||
|
||||
protected @Nullable List<String> headerComments;
|
||||
protected @Nullable String inlineComment;
|
||||
|
||||
protected @Nullable T defaultValue;
|
||||
|
||||
public AbstractConfigBuilder(Class<? super P> providerClass) {
|
||||
this.providerClass = providerClass;
|
||||
}
|
||||
|
||||
protected abstract @NotNull B getThis();
|
||||
|
||||
public abstract @NotNull ConfigValue<?> build();
|
||||
|
||||
public @NotNull B from(@Nullable P provider) {
|
||||
this.provider = provider;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public @NotNull B path(@Nullable String path) {
|
||||
this.path = path;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public @NotNull B comments(@NotNull String... comments) {
|
||||
return headerComments(comments);
|
||||
}
|
||||
|
||||
public @NotNull B headerComments(@NotNull String... comments) {
|
||||
return headerComments(Arrays.asList(comments));
|
||||
}
|
||||
|
||||
public @NotNull B headerComments(@NotNull List<String> comments) {
|
||||
this.headerComments = comments;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public @NotNull B inlineComment(@NotNull String comment) {
|
||||
this.inlineComment = comment;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public @NotNull B defaults(@Nullable T defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return getThis();
|
||||
}
|
||||
|
||||
public @NotNull B defaults(@NotNull Supplier<@Nullable T> defaultValueSupplier) {
|
||||
return defaults(defaultValueSupplier.get());
|
||||
}
|
||||
|
||||
protected @NotNull ValueManifest<T> buildManifest() {
|
||||
return ValueManifest.of(
|
||||
this.provider, this.path,
|
||||
this.headerComments, this.inlineComment, this.defaultValue
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
|
||||
public abstract class CommonConfigBuilder<T, B extends CommonConfigBuilder<T, B>>
|
||||
extends AbstractConfigBuilder<T, B, ConfigurationProvider<?>> {
|
||||
|
||||
public CommonConfigBuilder() {
|
||||
super(ConfigurationProvider.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder;
|
||||
import cc.carm.lib.configuration.core.builder.map.ConfigMapBuilder;
|
||||
import cc.carm.lib.configuration.core.builder.map.ConfigMapCreator;
|
||||
import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class ConfigBuilder {
|
||||
|
||||
public <V> @NotNull ConfigValueBuilder<V> asValue(@NotNull Class<V> valueClass) {
|
||||
return new ConfigValueBuilder<>(valueClass);
|
||||
}
|
||||
|
||||
public <V> @NotNull ConfigListBuilder<V> asList(@NotNull Class<V> valueClass) {
|
||||
return new ConfigListBuilder<>(valueClass);
|
||||
}
|
||||
|
||||
public <K, V> @NotNull ConfigMapCreator<K, V> asMap(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return new ConfigMapCreator<>(keyClass, valueClass);
|
||||
}
|
||||
|
||||
public <K, V> @NotNull ConfigMapBuilder<HashMap<K, V>, K, V> asHashMap(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return asMap(keyClass, valueClass).asHashMap();
|
||||
}
|
||||
|
||||
public <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asLinkedMap(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return asMap(keyClass, valueClass).asLinkedMap();
|
||||
}
|
||||
|
||||
public <K extends Comparable<K>, V> @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return asMap(keyClass, valueClass).asTreeMap();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.list;
|
||||
|
||||
import cc.carm.lib.configuration.core.annotation.InlineComment;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class ConfigListBuilder<V> {
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
public ConfigListBuilder(@NotNull Class<V> valueClass) {
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceListBuilder<S, V> from(@NotNull Class<S> sourceClass,
|
||||
@NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull ConfigDataFunction<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
return new SourceListBuilder<>(sourceClass, sourceParser, this.valueClass, valueParser, valueSerializer, sourceSerializer);
|
||||
}
|
||||
|
||||
|
||||
public @NotNull <S> SourceListBuilder<S, V> from(Class<S> sourceClass) {
|
||||
return from(sourceClass,
|
||||
ConfigDataFunction.required(), ConfigDataFunction.required(),
|
||||
ConfigDataFunction.required(), ConfigDataFunction.required()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<Object, V> fromObject() {
|
||||
return from(
|
||||
Object.class, ConfigDataFunction.identity(),
|
||||
ConfigDataFunction.castObject(valueClass),
|
||||
ConfigDataFunction.toObject(), ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<String, V> fromString() {
|
||||
return from(
|
||||
String.class, ConfigDataFunction.castToString(),
|
||||
ConfigDataFunction.castFromString(this.valueClass),
|
||||
ConfigDataFunction.castToString(), ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.list;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
public class SourceListBuilder<S, V> extends CommonConfigBuilder<List<V>, SourceListBuilder<S, V>> {
|
||||
|
||||
protected final @NotNull Class<S> sourceClass;
|
||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
protected @NotNull ConfigDataFunction<S, V> valueParser;
|
||||
|
||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
||||
|
||||
public SourceListBuilder(@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
this.sourceClass = sourceClass;
|
||||
this.sourceParser = sourceParser;
|
||||
this.sourceSerializer = sourceSerializer;
|
||||
this.valueClass = valueClass;
|
||||
this.valueParser = valueParser;
|
||||
this.valueSerializer = valueSerializer;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final @NotNull SourceListBuilder<S, V> defaults(@NotNull V... values) {
|
||||
return defaults(new ArrayList<>(Arrays.asList(values)));
|
||||
}
|
||||
|
||||
public final @NotNull SourceListBuilder<S, V> defaults(@NotNull Collection<V> values) {
|
||||
return defaults(new ArrayList<>(values));
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<S, V> parseSource(ConfigDataFunction<Object, S> sourceParser) {
|
||||
this.sourceParser = sourceParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<S, V> parseValue(ConfigDataFunction<S, V> valueParser) {
|
||||
this.valueParser = valueParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<S, V> serializeValue(ConfigDataFunction<V, S> serializer) {
|
||||
this.valueSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<S, V> serializeSource(ConfigDataFunction<S, Object> serializer) {
|
||||
this.sourceSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceListBuilder<S, V> getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredList<V> build() {
|
||||
return new ConfiguredList<>(
|
||||
buildManifest(), this.valueClass,
|
||||
this.sourceParser.andThen(this.valueParser),
|
||||
this.valueSerializer.andThen(sourceSerializer)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
|
||||
|
||||
protected final @NotNull Supplier<@NotNull M> supplier;
|
||||
|
||||
protected final @NotNull Class<K> keyClass;
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
public ConfigMapBuilder(@NotNull Supplier<@NotNull M> supplier, @NotNull Class<K> keyClass, @NotNull Class<V> valueClass) {
|
||||
this.supplier = supplier;
|
||||
this.keyClass = keyClass;
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
public <MAP extends Map<K, V>> ConfigMapBuilder<MAP, K, V> supplier(@NotNull Supplier<MAP> supplier) {
|
||||
return new ConfigMapBuilder<>(supplier, keyClass, valueClass);
|
||||
}
|
||||
|
||||
public <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> sourceClass,
|
||||
@NotNull ConfigDataFunction<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer) {
|
||||
return new SourceMapBuilder<>(supplier,
|
||||
keyClass, ConfigDataFunction.castFromString(this.keyClass), // #String -> key
|
||||
sourceClass, ConfigDataFunction.castObject(sourceClass), // #Object -> source
|
||||
valueClass, valueParser, // source -> value
|
||||
ConfigDataFunction.castToString(), // key -> #String
|
||||
valueSerializer/*value -> source*/,
|
||||
ConfigDataFunction.toObject()/* source -> #Object */
|
||||
);
|
||||
}
|
||||
|
||||
public <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> sourceClass) {
|
||||
return from(sourceClass, ConfigDataFunction.required(), ConfigDataFunction.required());
|
||||
}
|
||||
|
||||
public SourceMapBuilder<M, String, K, V> fromString(@NotNull ConfigDataFunction<String, V> valueParser) {
|
||||
return from(String.class, valueParser, ConfigDataFunction.castToString());
|
||||
}
|
||||
|
||||
public SourceMapBuilder<M, String, K, V> fromString() {
|
||||
return fromString(ConfigDataFunction.castFromString(this.valueClass));
|
||||
}
|
||||
|
||||
public SourceMapBuilder<M, Object, K, V> fromObject(@NotNull ConfigDataFunction<Object, V> valueParser) {
|
||||
return from(Object.class, valueParser, ConfigDataFunction.toObject());
|
||||
}
|
||||
|
||||
public SourceMapBuilder<M, Object, K, V> fromObject() {
|
||||
return fromObject(ConfigDataFunction.required());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.map;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigMapCreator<K, V> {
|
||||
|
||||
protected final @NotNull Class<K> keyClass;
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
public ConfigMapCreator(@NotNull Class<K> keyClass, @NotNull Class<V> valueClass) {
|
||||
this.keyClass = keyClass;
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
public <M extends Map<K, V>> @NotNull ConfigMapBuilder<M, K, V> asMap(Supplier<M> mapSuppler) {
|
||||
return new ConfigMapBuilder<>(mapSuppler, keyClass, valueClass);
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<HashMap<K, V>, K, V> asHashMap() {
|
||||
return asMap(HashMap::new);
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asLinkedMap() {
|
||||
return asMap(LinkedHashMap::new);
|
||||
}
|
||||
|
||||
public @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap() {
|
||||
return asMap(TreeMap::new);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SourceMapBuilder<M extends Map<K, V>, S, K, V> extends CommonConfigBuilder<M, SourceMapBuilder<M, S, K, V>> {
|
||||
|
||||
protected final @NotNull Supplier<@NotNull M> supplier;
|
||||
|
||||
protected final @NotNull Class<K> keyClass;
|
||||
protected @NotNull ConfigDataFunction<String, K> keyParser;
|
||||
|
||||
protected final @NotNull Class<S> sourceClass;
|
||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
protected @NotNull ConfigDataFunction<S, V> valueParser;
|
||||
|
||||
protected @NotNull ConfigDataFunction<K, String> keySerializer;
|
||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
||||
|
||||
public SourceMapBuilder(@NotNull Supplier<@NotNull M> supplier,
|
||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
||||
@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
this.supplier = supplier;
|
||||
this.keyClass = keyClass;
|
||||
this.keyParser = keyParser;
|
||||
this.valueClass = valueClass;
|
||||
this.valueParser = valueParser;
|
||||
this.sourceClass = sourceClass;
|
||||
this.sourceParser = sourceParser;
|
||||
this.keySerializer = keySerializer;
|
||||
this.valueSerializer = valueSerializer;
|
||||
this.sourceSerializer = sourceSerializer;
|
||||
}
|
||||
|
||||
public <MAP extends Map<K, V>> SourceMapBuilder<MAP, S, K, V> supplier(@NotNull Supplier<MAP> supplier) {
|
||||
return new SourceMapBuilder<>(supplier,
|
||||
keyClass, keyParser, sourceClass, sourceParser, valueClass, valueParser,
|
||||
keySerializer, valueSerializer, sourceSerializer
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> defaults(@NotNull Consumer<M> factory) {
|
||||
M map = supplier.get();
|
||||
factory.accept(map);
|
||||
return defaults(map);
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> parseKey(@NotNull ConfigDataFunction<String, K> parser) {
|
||||
this.keyParser = parser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> parseSource(@NotNull ConfigDataFunction<Object, S> parser) {
|
||||
this.sourceParser = parser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> parseValue(@NotNull ConfigDataFunction<S, V> parser) {
|
||||
this.valueParser = parser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> serializeKey(@NotNull ConfigDataFunction<K, String> serializer) {
|
||||
this.keySerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> serializeValue(@NotNull ConfigDataFunction<V, S> serializer) {
|
||||
this.valueSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceMapBuilder<M, S, K, V> serializeSource(@NotNull ConfigDataFunction<S, Object> serializer) {
|
||||
this.sourceSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceMapBuilder<M, S, K, V> getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredMap<K, V> build() {
|
||||
return new ConfiguredMap<>(
|
||||
new ValueManifest<>(provider, path, headerComments, inlineComment, defaultValue),
|
||||
this.supplier, this.keyClass, this.keyParser,
|
||||
this.valueClass, this.sourceParser.andThen(this.valueParser),
|
||||
this.keySerializer, this.valueSerializer.andThen(this.sourceSerializer)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigValueBuilder<V> {
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
public ConfigValueBuilder(@NotNull Class<V> valueClass) {
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection() {
|
||||
return fromSection(ConfigValueParser.required(), ConfigDataFunction.required());
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection(@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer) {
|
||||
return new SectionValueBuilder<>(this.valueClass, valueParser, valueSerializer);
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(Class<S> sourceClass) {
|
||||
return from(
|
||||
sourceClass, ConfigDataFunction.required(), ConfigValueParser.required(),
|
||||
ConfigDataFunction.required(), ConfigDataFunction.required()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull Class<S> sourceClass,
|
||||
@NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull ConfigValueParser<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
return new SourceValueBuilder<>(
|
||||
sourceClass, sourceParser, this.valueClass, valueParser,
|
||||
valueSerializer, sourceSerializer
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<Object, V> fromObject() {
|
||||
return from(
|
||||
Object.class, ConfigDataFunction.identity(),
|
||||
ConfigValueParser.castObject(valueClass),
|
||||
ConfigDataFunction.toObject(), ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<String, V> fromString() {
|
||||
return from(
|
||||
String.class, ConfigDataFunction.castToString(),
|
||||
ConfigValueParser.parseString(this.valueClass),
|
||||
ConfigDataFunction.castToString(), ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SectionValueBuilder<V>
|
||||
extends CommonConfigBuilder<V, SectionValueBuilder<V>> {
|
||||
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser;
|
||||
protected @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
|
||||
|
||||
public SectionValueBuilder(@NotNull Class<V> valueClass,
|
||||
@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser,
|
||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected @NotNull SectionValueBuilder<V> getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> parseValue(ConfigValueParser<ConfigurationWrapper<?>, V> valueParser) {
|
||||
this.parser = valueParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> serializeValue(ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
||||
this.serializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredSection<V> build() {
|
||||
return new ConfiguredSection<>(buildManifest(), this.valueClass, this.parser, this.serializer);
|
||||
}
|
||||
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.CommonConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SourceValueBuilder<S, V> extends CommonConfigBuilder<V, SourceValueBuilder<S, V>> {
|
||||
|
||||
protected final @NotNull Class<S> sourceClass;
|
||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
protected @NotNull ConfigValueParser<S, V> valueParser;
|
||||
|
||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
||||
|
||||
public SourceValueBuilder(@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
@NotNull Class<V> valueClass, @NotNull ConfigValueParser<S, V> valueParser,
|
||||
@NotNull ConfigDataFunction<V, S> valueSerializer,
|
||||
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
|
||||
this.sourceClass = sourceClass;
|
||||
this.sourceParser = sourceParser;
|
||||
this.valueClass = valueClass;
|
||||
this.valueParser = valueParser;
|
||||
this.sourceSerializer = sourceSerializer;
|
||||
this.valueSerializer = valueSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SourceValueBuilder<S, V> getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<S, V> parseSource(@NotNull ConfigDataFunction<Object, S> sourceParser) {
|
||||
this.sourceParser = sourceParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<S, V> parseValue(@NotNull ConfigValueParser<S, V> valueParser) {
|
||||
this.valueParser = valueParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<S, V> serializeValue(@NotNull ConfigDataFunction<V, S> serializer) {
|
||||
this.valueSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SourceValueBuilder<S, V> serializeSource(@NotNull ConfigDataFunction<S, Object> serializer) {
|
||||
this.sourceSerializer = serializer;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredValue<V> build() {
|
||||
return new ConfiguredValue<>(
|
||||
buildManifest(), this.valueClass,
|
||||
this.valueParser.compose(this.sourceParser),
|
||||
this.valueSerializer.andThen(sourceSerializer)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.function;
|
||||
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigValueParser<T, R> {
|
||||
|
||||
@Nullable R parse(@NotNull T data, @Nullable R defaultValue) throws Exception;
|
||||
|
||||
default <V> ConfigValueParser<T, V> andThen(@NotNull ConfigValueParser<R, V> after) {
|
||||
Objects.requireNonNull(after);
|
||||
return ((data, defaultValue) -> {
|
||||
R result = parse(data, null);
|
||||
if (result == null) return defaultValue;
|
||||
else return after.parse(result, defaultValue);
|
||||
});
|
||||
}
|
||||
|
||||
default <V> ConfigValueParser<V, R> compose(@NotNull ConfigDataFunction<? super V, ? extends T> before) {
|
||||
Objects.requireNonNull(before);
|
||||
return ((data, defaultValue) -> {
|
||||
T result = before.parse(data);
|
||||
return parse(result, defaultValue);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigValueParser<T, T> identity() {
|
||||
return (input, defaultValue) -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigValueParser<T, Object> toObject() {
|
||||
return (input, defaultValue) -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull ConfigValueParser<T, V> required() {
|
||||
return (input, defaultValue) -> {
|
||||
throw new IllegalArgumentException("Please specify the value parser.");
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigValueParser<Object, V> castObject(Class<V> valueClass) {
|
||||
return (input, defaultValue) -> {
|
||||
|
||||
if (Number.class.isAssignableFrom(valueClass)) {
|
||||
if (Long.class.isAssignableFrom(valueClass)) {
|
||||
input = longValue().parse(input, (Long) defaultValue);
|
||||
} else if (Integer.class.isAssignableFrom(valueClass)) {
|
||||
input = intValue().parse(input, (Integer) defaultValue);
|
||||
} else if (Float.class.isAssignableFrom(valueClass)) {
|
||||
input = floatValue().parse(input, (Float) defaultValue);
|
||||
} else if (Double.class.isAssignableFrom(valueClass)) {
|
||||
input = doubleValue().parse(input, (Double) defaultValue);
|
||||
} else if (Byte.class.isAssignableFrom(valueClass)) {
|
||||
input = byteValue().parse(input, (Byte) defaultValue);
|
||||
} else if (Short.class.isAssignableFrom(valueClass)) {
|
||||
input = shortValue().parse(input, (Short) defaultValue);
|
||||
}
|
||||
} else if (Boolean.class.isAssignableFrom(valueClass)) {
|
||||
input = booleanValue().parse(input, (Boolean) defaultValue);
|
||||
}
|
||||
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigValueParser<String, V> parseString(Class<V> valueClass) {
|
||||
return (input, defaultValue) -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, String> castToString() {
|
||||
return (input, defaultValue) -> {
|
||||
if (input instanceof String) return (String) input;
|
||||
else return input.toString();
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Integer> intValue() {
|
||||
return (input, defaultValue) -> ConfigDataFunction.intValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Short> shortValue() {
|
||||
return (input, defaultValue) -> ConfigDataFunction.shortValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Double> doubleValue() {
|
||||
return (input, defaultValue) -> ConfigDataFunction.doubleValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Byte> byteValue() {
|
||||
return (input, defaultValue) -> ConfigDataFunction.byteValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Float> floatValue() {
|
||||
return (input, defaultValue) -> ConfigDataFunction.floatValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Long> longValue() {
|
||||
return (input, defaultValue) -> ConfigDataFunction.longValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Boolean> booleanValue() {
|
||||
return (input, defaultValue) -> ConfigDataFunction.booleanValue().parse(input);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.source;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class ConfigurationComments {
|
||||
|
||||
protected final @NotNull Map<String, List<String>> headerComments = new HashMap<>();
|
||||
protected final @NotNull Map<String, String> inlineComments = new HashMap<>();
|
||||
|
||||
protected @NotNull Map<String, List<String>> getHeaderComments() {
|
||||
return headerComments;
|
||||
}
|
||||
|
||||
protected @NotNull Map<String, String> getInlineComments() {
|
||||
return inlineComments;
|
||||
}
|
||||
|
||||
public void setHeaderComments(@Nullable String path, @Nullable List<String> comments) {
|
||||
|
||||
if (comments == null) {
|
||||
getHeaderComments().remove(path);
|
||||
} else {
|
||||
getHeaderComments().put(path, comments);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setInlineComment(@NotNull String path, @Nullable String comment) {
|
||||
if (comment == null) {
|
||||
getInlineComments().remove(path);
|
||||
} else {
|
||||
getInlineComments().put(path, comment);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Unmodifiable
|
||||
public List<String> getHeaderComment(@Nullable String path) {
|
||||
return Optional.ofNullable(getHeaderComments().get(path)).map(Collections::unmodifiableList).orElse(null);
|
||||
}
|
||||
|
||||
public @Nullable String getInlineComment(@NotNull String path) {
|
||||
return getInlineComments().get(path);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.source;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 配置文件提供者,用于为 {@link ConfigValue} 提供配置文件的源,以便实现读取、保存等操作。
|
||||
*
|
||||
* @param <W> 配置文件的原生功能类
|
||||
*/
|
||||
public abstract class ConfigurationProvider<W extends ConfigurationWrapper<?>> {
|
||||
|
||||
protected long updateTime;
|
||||
|
||||
protected ConfigurationProvider() {
|
||||
this.updateTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到配置文件的更新(最后加载)时间。
|
||||
*
|
||||
* @return 更新时间
|
||||
*/
|
||||
public long getUpdateTime() {
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于 {@link CachedConfigValue} 判断缓存值是否过期(即缓存的时间早于配置文件的最后加载时间)。
|
||||
*
|
||||
* @param time 缓存值时的时间戳
|
||||
* @return 缓存值是否过期
|
||||
*/
|
||||
public boolean isExpired(long time) {
|
||||
return getUpdateTime() > time;
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到配置文件的原生功能类。
|
||||
*
|
||||
* @return 原生类
|
||||
*/
|
||||
public abstract @NotNull W getConfiguration();
|
||||
|
||||
/**
|
||||
* 重载当前配置文件。(将不会保存已修改的内容)
|
||||
*
|
||||
* @throws Exception 当重载出现错误时抛出
|
||||
*/
|
||||
public void reload() throws Exception {
|
||||
onReload(); // 调用重写的Reload方法
|
||||
this.updateTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前对配置文件的更改进行保存。
|
||||
*
|
||||
* @throws Exception 当保存出现错误时抛出
|
||||
*/
|
||||
public abstract void save() throws Exception;
|
||||
|
||||
/**
|
||||
* 针对于不同配置文件类型所执行的重载操作。
|
||||
*
|
||||
* @throws Exception 当操作出现错误时抛出。
|
||||
*/
|
||||
protected abstract void onReload() throws Exception;
|
||||
|
||||
public abstract @Nullable ConfigurationComments getComments();
|
||||
|
||||
public void setHeaderComment(@Nullable String path, @Nullable List<String> comments) {
|
||||
if (getComments() == null) return;
|
||||
getComments().setHeaderComments(path, comments);
|
||||
}
|
||||
|
||||
public void setInlineComment(@NotNull String path, @Nullable String comment) {
|
||||
if (getComments() == null) return;
|
||||
getComments().setInlineComment(path, comment);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Unmodifiable
|
||||
public List<String> getHeaderComment(@Nullable String path) {
|
||||
return Optional.ofNullable(getComments()).map(c -> c.getHeaderComment(path)).orElse(null);
|
||||
}
|
||||
|
||||
public @Nullable String getInlineComment(@NotNull String path) {
|
||||
return Optional.ofNullable(getComments()).map(c -> c.getInlineComment(path)).orElse(null);
|
||||
}
|
||||
|
||||
public abstract @NotNull ConfigInitializer<? extends ConfigurationProvider<W>> getInitializer();
|
||||
|
||||
/**
|
||||
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
||||
*
|
||||
* @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。
|
||||
*/
|
||||
public void initialize(Class<? extends ConfigurationRoot> configClazz) {
|
||||
initialize(configClazz, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
||||
*
|
||||
* @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。
|
||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
||||
*/
|
||||
public void initialize(Class<? extends ConfigurationRoot> configClazz, boolean saveDefaults) {
|
||||
this.getInitializer().initialize(configClazz, saveDefaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化指定类的所有 {@link ConfigValue} 对象。
|
||||
*
|
||||
* @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。
|
||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
||||
* @param loadSubClasses 是否加载内部子类(默认为 true)。
|
||||
*/
|
||||
public void initialize(Class<? extends ConfigurationRoot> configClazz, boolean saveDefaults, boolean loadSubClasses) {
|
||||
this.getInitializer().initialize(configClazz, saveDefaults, loadSubClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。
|
||||
*
|
||||
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。
|
||||
*/
|
||||
public void initialize(@NotNull ConfigurationRoot config) {
|
||||
this.getInitializer().initialize(config, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。
|
||||
*
|
||||
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。
|
||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
||||
*/
|
||||
public void initialize(@NotNull ConfigurationRoot config, boolean saveDefaults) {
|
||||
this.getInitializer().initialize(config, saveDefaults);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.source;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
interface ConfigurationReader {
|
||||
|
||||
ConfigurationWrapper<?> getWrapper();
|
||||
|
||||
default boolean isBoolean(@NotNull String path) {
|
||||
return getWrapper().isType(path, Boolean.class);
|
||||
}
|
||||
|
||||
default boolean getBoolean(@NotNull String path) {
|
||||
return getBoolean(path, false);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.booleanValue());
|
||||
}
|
||||
|
||||
default @Nullable Boolean isByte(@NotNull String path) {
|
||||
return getWrapper().isType(path, Byte.class);
|
||||
}
|
||||
|
||||
default @Nullable Byte getByte(@NotNull String path) {
|
||||
return getByte(path, (byte) 0);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.byteValue());
|
||||
}
|
||||
|
||||
default boolean isShort(@NotNull String path) {
|
||||
return getWrapper().isType(path, Short.class);
|
||||
}
|
||||
|
||||
default @Nullable Short getShort(@NotNull String path) {
|
||||
return getShort(path, (short) 0);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.shortValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isInt(@NotNull String path) {
|
||||
return getWrapper().isType(path, Integer.class);
|
||||
}
|
||||
|
||||
default @Nullable Integer getInt(@NotNull String path) {
|
||||
return getInt(path, 0);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.intValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isLong(@NotNull String path) {
|
||||
return getWrapper().isType(path, Long.class);
|
||||
}
|
||||
|
||||
default @Nullable Long getLong(@NotNull String path) {
|
||||
return getLong(path, 0L);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.longValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isFloat(@NotNull String path) {
|
||||
return getWrapper().isType(path, Float.class);
|
||||
}
|
||||
|
||||
default @Nullable Float getFloat(@NotNull String path) {
|
||||
return getFloat(path, 0.0F);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.floatValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isDouble(@NotNull String path) {
|
||||
return getWrapper().isType(path, Double.class);
|
||||
}
|
||||
|
||||
default @Nullable Double getDouble(@NotNull String path) {
|
||||
return getDouble(path, 0.0D);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
|
||||
return getWrapper().get(path, def, ConfigValueParser.doubleValue());
|
||||
}
|
||||
|
||||
|
||||
default boolean isChar(@NotNull String path) {
|
||||
return getWrapper().isType(path, Boolean.class);
|
||||
}
|
||||
|
||||
default @Nullable Character getChar(@NotNull String path) {
|
||||
return getChar(path, null);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
|
||||
return getWrapper().get(path, def, Character.class);
|
||||
}
|
||||
|
||||
|
||||
default boolean isString(@NotNull String path) {
|
||||
return getWrapper().isType(path, String.class);
|
||||
}
|
||||
|
||||
default @Nullable String getString(@NotNull String path) {
|
||||
return getString(path, null);
|
||||
}
|
||||
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable String getString(@NotNull String path, @Nullable String def) {
|
||||
return getWrapper().get(path, def, String.class);
|
||||
}
|
||||
|
||||
@Unmodifiable
|
||||
default @NotNull List<String> getStringList(@NotNull String path) {
|
||||
return parseList(getWrapper().getList(path), ConfigValueParser.castToString());
|
||||
}
|
||||
|
||||
@Unmodifiable
|
||||
default @NotNull List<Integer> getIntegerList(@NotNull String path) {
|
||||
return parseList(getWrapper().getList(path), ConfigValueParser.intValue());
|
||||
}
|
||||
|
||||
@Unmodifiable
|
||||
default @NotNull List<Long> getLongList(@NotNull String path) {
|
||||
return parseList(getWrapper().getList(path), ConfigValueParser.longValue());
|
||||
}
|
||||
|
||||
@Unmodifiable
|
||||
default @NotNull List<Double> getDoubleList(@NotNull String path) {
|
||||
return parseList(getWrapper().getList(path), ConfigValueParser.doubleValue());
|
||||
}
|
||||
|
||||
@Unmodifiable
|
||||
default @NotNull List<Float> getFloatList(@NotNull String path) {
|
||||
return parseList(getWrapper().getList(path), ConfigValueParser.floatValue());
|
||||
}
|
||||
|
||||
@Unmodifiable
|
||||
default @NotNull List<Byte> getByteList(@NotNull String path) {
|
||||
return parseList(getWrapper().getList(path), ConfigValueParser.byteValue());
|
||||
}
|
||||
|
||||
@Unmodifiable
|
||||
default @NotNull List<Character> getCharList(@NotNull String path) {
|
||||
return parseList(getWrapper().getList(path), ConfigValueParser.castObject(Character.class));
|
||||
}
|
||||
|
||||
@Unmodifiable
|
||||
static <T> @NotNull List<T> parseList(@Nullable List<?> list, ConfigValueParser<Object, T> parser) {
|
||||
if (list == null) return Collections.emptyList();
|
||||
List<T> values = new ArrayList<>();
|
||||
for (Object o : list) {
|
||||
try {
|
||||
T parsed = parser.parse(o, null);
|
||||
if (parsed != null) values.add(parsed);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.source;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ConfigurationWrapper<S> extends ConfigurationReader {
|
||||
|
||||
@Override
|
||||
default ConfigurationWrapper<S> getWrapper() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull S getSource();
|
||||
|
||||
@NotNull
|
||||
Set<String> getKeys(boolean deep);
|
||||
|
||||
@NotNull
|
||||
Map<String, Object> getValues(boolean deep);
|
||||
|
||||
void set(@NotNull String path, @Nullable Object value);
|
||||
|
||||
boolean contains(@NotNull String path);
|
||||
|
||||
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
|
||||
return typeClass.isInstance(get(path));
|
||||
}
|
||||
|
||||
@Nullable Object get(@NotNull String path);
|
||||
|
||||
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 ConfigValueParser<Object, T> parser) {
|
||||
return get(path, null, parser);
|
||||
}
|
||||
|
||||
@Contract("_,!null,_->!null")
|
||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class<T> clazz) {
|
||||
return get(path, defaultValue, ConfigValueParser.castObject(clazz));
|
||||
}
|
||||
|
||||
@Contract("_,!null,_->!null")
|
||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
|
||||
@NotNull ConfigValueParser<Object, T> parser) {
|
||||
Object value = get(path);
|
||||
if (value != null) {
|
||||
try {
|
||||
return parser.parse(value, defaultValue);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
boolean isList(@NotNull String path);
|
||||
|
||||
@Nullable List<?> getList(@NotNull String path);
|
||||
|
||||
boolean isConfigurationSection(@NotNull String path);
|
||||
|
||||
@Nullable
|
||||
ConfigurationWrapper<S> getConfigurationSection(@NotNull String path);
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.source.impl;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class FileConfigProvider<W extends ConfigurationWrapper<?>> extends ConfigurationProvider<W> {
|
||||
|
||||
protected final @NotNull File file;
|
||||
|
||||
protected FileConfigProvider(@NotNull File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
public @NotNull File getFile() {
|
||||
return file;
|
||||
}
|
||||
|
||||
public void initializeFile(@Nullable String sourcePath) throws IOException {
|
||||
if (this.file.exists()) return;
|
||||
|
||||
File parent = this.file.getParentFile();
|
||||
if (parent != null && !parent.exists() && !parent.mkdirs()) {
|
||||
throw new IOException("Failed to create directory " + file.getParentFile().getAbsolutePath());
|
||||
}
|
||||
|
||||
if (!this.file.createNewFile()) {
|
||||
throw new IOException("Failed to create file " + file.getAbsolutePath());
|
||||
}
|
||||
|
||||
if (sourcePath != null) {
|
||||
try {
|
||||
saveResource(sourcePath, true);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void saveResource(@NotNull String resourcePath, boolean replace)
|
||||
throws NullPointerException, IOException, IllegalArgumentException {
|
||||
Objects.requireNonNull(resourcePath, "ResourcePath cannot be null");
|
||||
if (resourcePath.equals("")) throw new IllegalArgumentException("ResourcePath cannot be empty");
|
||||
|
||||
resourcePath = resourcePath.replace('\\', '/');
|
||||
InputStream in = getResource(resourcePath);
|
||||
if (in == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists");
|
||||
|
||||
|
||||
int lastIndex = resourcePath.lastIndexOf('/');
|
||||
File outDir = new File(file, resourcePath.substring(0, Math.max(lastIndex, 0)));
|
||||
|
||||
if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir);
|
||||
if (!file.exists() || replace) {
|
||||
try {
|
||||
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
byte[] buf = new byte[1024];
|
||||
int len;
|
||||
while ((len = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
out.close();
|
||||
in.close();
|
||||
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public InputStream getResource(@NotNull String filename) {
|
||||
try {
|
||||
URL url = this.getClass().getClassLoader().getResource(filename);
|
||||
if (url == null) return null;
|
||||
URLConnection connection = url.openConnection();
|
||||
connection.setUseCaches(false);
|
||||
return connection.getInputStream();
|
||||
} catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class MapFactory<S extends Map<K, V>, K, V> {
|
||||
|
||||
private final S map;
|
||||
|
||||
protected MapFactory(S map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public MapFactory<S, K, V> put(K key, V value) {
|
||||
this.map.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapFactory<S, K, V> remove(K key) {
|
||||
this.map.remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MapFactory<S, K, V> clear() {
|
||||
this.map.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
public S build() {
|
||||
return get();
|
||||
}
|
||||
|
||||
public S get() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static <K, V> MapFactory<HashMap<K, V>, K, V> hashMap() {
|
||||
return new MapFactory<>(new HashMap<>());
|
||||
}
|
||||
|
||||
public static <K, V> MapFactory<HashMap<K, V>, K, V> hashMap(K firstKey, V firstValue) {
|
||||
return MapFactory.<K, V>hashMap().put(firstKey, firstValue);
|
||||
}
|
||||
|
||||
public static <K, V> MapFactory<LinkedHashMap<K, V>, K, V> linkedMap() {
|
||||
return of(new LinkedHashMap<>());
|
||||
}
|
||||
|
||||
public static <K, V> MapFactory<LinkedHashMap<K, V>, K, V> linkedMap(K firstKey, V firstValue) {
|
||||
return MapFactory.<K, V>linkedMap().put(firstKey, firstValue);
|
||||
}
|
||||
|
||||
public static <K extends Comparable<K>, V> MapFactory<TreeMap<K, V>, K, V> treeMap() {
|
||||
return of(new TreeMap<>());
|
||||
}
|
||||
|
||||
public static <K extends Comparable<K>, V> MapFactory<TreeMap<K, V>, K, V> treeMap(K firstKey, V firstValue) {
|
||||
return MapFactory.<K, V>treeMap().put(firstKey, firstValue);
|
||||
}
|
||||
|
||||
public static <M extends Map<K, V>, K, V> MapFactory<M, K, V> of(M map) {
|
||||
return new MapFactory<>(map);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class ConfigValue<T> extends ValueManifest<T> {
|
||||
|
||||
public static @NotNull ConfigBuilder builder() {
|
||||
return new ConfigBuilder();
|
||||
}
|
||||
|
||||
protected ConfigValue(@NotNull ValueManifest<T> manifest) {
|
||||
super(manifest.provider, manifest.configPath, manifest.headerComments, manifest.inlineComment, manifest.defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param provider 配置文件提供者
|
||||
* @param configPath 配置路径
|
||||
* @param headerComments 头部注释内容
|
||||
* @param inlineComments 行内注释内容
|
||||
* @param defaultValue 默认参数值
|
||||
* @deprecated 请使用 {@link #ConfigValue(ValueManifest)} 构造器。
|
||||
*/
|
||||
@Deprecated
|
||||
protected ConfigValue(@Nullable ConfigurationProvider<?> provider, @Nullable String configPath,
|
||||
@Nullable List<String> headerComments, @Nullable String inlineComments,
|
||||
@Nullable T defaultValue) {
|
||||
super(provider, configPath, headerComments, inlineComments, defaultValue);
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ConfigurationProvider<?> provider, boolean saveDefault, @NotNull String configPath,
|
||||
@Nullable List<String> headerComments, @Nullable String inlineComments) {
|
||||
this.initialize(provider, configPath, headerComments, inlineComments);
|
||||
if (saveDefault) setDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到该配置的设定值(即读取到的值)。
|
||||
* <br> 若初始化时未写入默认值,则可以通过 {@link #getOrDefault()} 方法在该设定值为空时获取默认值。
|
||||
*
|
||||
* @return 设定值
|
||||
*/
|
||||
public abstract @Nullable T get();
|
||||
|
||||
/**
|
||||
* 得到该配置的设定值,若不存在,则返回默认值。
|
||||
*
|
||||
* @return 设定值或默认值
|
||||
*/
|
||||
public @Nullable T getOrDefault() {
|
||||
return getOptional().orElse(getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到该配置的非空值。
|
||||
*
|
||||
* @return 非空值
|
||||
* @throws NullPointerException 对应数据为空时抛出
|
||||
*/
|
||||
public @NotNull T getNotNull() {
|
||||
return Objects.requireNonNull(getOrDefault(), "Value(" + configPath + ") is null.");
|
||||
}
|
||||
|
||||
public @NotNull Optional<@Nullable T> getOptional() {
|
||||
return Optional.ofNullable(get());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设定该配置的值。
|
||||
* <br> 设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationProvider#save()} 方法。
|
||||
*
|
||||
* @param value 配置的值
|
||||
*/
|
||||
public abstract void set(@Nullable T value);
|
||||
|
||||
/**
|
||||
* 初始化该配置的默认值。
|
||||
* <br> 设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationProvider#save()} 方法。
|
||||
*/
|
||||
public void setDefault() {
|
||||
setDefault(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将该配置的值设置为默认值。
|
||||
* <br> 设定后,不会自动保存配置文件;若需要保存,请调用 {@link ConfigurationProvider#save()} 方法。
|
||||
*
|
||||
* @param override 是否覆盖已设定的值
|
||||
*/
|
||||
public void setDefault(boolean override) {
|
||||
if (!override && getConfiguration().contains(getConfigPath())) return;
|
||||
Optional.ofNullable(getDefaultValue()).ifPresent(this::set);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断加载的配置是否与默认值相同。
|
||||
*
|
||||
* @return 获取当前值是否为默认值。
|
||||
*/
|
||||
public boolean isDefault() {
|
||||
T defaultValue = getDefaultValue();
|
||||
T value = get();
|
||||
if (defaultValue == null && value == null) return true;
|
||||
else if (defaultValue != null && value != null) return defaultValue.equals(value);
|
||||
else return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 配置值描述清单。用于描述一个配置值的相关基础信息。
|
||||
*
|
||||
* @param <T> 配置值类型
|
||||
* @author CarmJos
|
||||
*/
|
||||
public class ValueManifest<T> {
|
||||
|
||||
public static <V> ValueManifest<V> of(@Nullable ConfigurationProvider<?> provider, @Nullable String configPath,
|
||||
@Nullable List<String> headerComments, @Nullable String inlineComments) {
|
||||
return new ValueManifest<>(provider, configPath, headerComments, inlineComments, null);
|
||||
}
|
||||
|
||||
public static <V> ValueManifest<V> of(@Nullable ConfigurationProvider<?> provider, @Nullable String configPath,
|
||||
@Nullable List<String> headerComments, @Nullable String inlineComments,
|
||||
@Nullable V defaultValue) {
|
||||
return new ValueManifest<>(provider, configPath, headerComments, inlineComments, defaultValue);
|
||||
}
|
||||
|
||||
protected @Nullable ConfigurationProvider<?> provider;
|
||||
protected @Nullable String configPath;
|
||||
|
||||
protected @Nullable List<String> headerComments;
|
||||
protected @Nullable String inlineComment;
|
||||
|
||||
protected @Nullable T defaultValue;
|
||||
|
||||
/**
|
||||
* @param provider 配置文件提供者
|
||||
* @param configPath 配置路径
|
||||
* @param headerComments 头部注释内容
|
||||
* @param inlineComment 行内注释内容
|
||||
* @param defaultValue 默认参数值
|
||||
*/
|
||||
public ValueManifest(@Nullable ConfigurationProvider<?> provider, @Nullable String configPath,
|
||||
@Nullable List<String> headerComments, @Nullable String inlineComment,
|
||||
@Nullable T defaultValue) {
|
||||
this.provider = provider;
|
||||
this.configPath = configPath;
|
||||
this.headerComments = headerComments;
|
||||
this.inlineComment = inlineComment;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 此方法提供给 {@link ConfigInitializer},以便对配置值的相关信息进行统一初始化操作。
|
||||
*
|
||||
* @param provider 配置文件提供者
|
||||
* @param configPath 配置路径
|
||||
* @param headerComments 头部注释内容
|
||||
* @param inlineComment 行内注释内容
|
||||
*/
|
||||
protected void initialize(@NotNull ConfigurationProvider<?> provider, @NotNull String configPath,
|
||||
@Nullable List<String> headerComments, @Nullable String inlineComment) {
|
||||
if (this.provider == null) this.provider = provider;
|
||||
if (this.configPath == null) this.configPath = configPath;
|
||||
if (this.headerComments == null) this.headerComments = headerComments;
|
||||
if (this.inlineComment == null) this.inlineComment = inlineComment;
|
||||
|
||||
if (getHeaderComments() != null) {
|
||||
this.provider.setHeaderComment(getConfigPath(), getHeaderComments());
|
||||
}
|
||||
if (getInlineComment() != null) {
|
||||
this.provider.setInlineComment(getConfigPath(), getInlineComment());
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable T getDefaultValue() {
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(@Nullable T defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationProvider<?> getProvider() {
|
||||
return Optional.ofNullable(this.provider)
|
||||
.orElseThrow(() -> new IllegalStateException("Value(" + configPath + ") does not have a provider."));
|
||||
}
|
||||
|
||||
public final @NotNull ConfigurationWrapper<?> getConfiguration() {
|
||||
try {
|
||||
return getProvider().getConfiguration();
|
||||
} catch (Exception ex) {
|
||||
throw new IllegalStateException("Value(" + configPath + ") has not been initialized", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public @NotNull String getConfigPath() {
|
||||
return Optional.ofNullable(this.configPath)
|
||||
.orElseThrow(() -> new IllegalStateException("No section path provided."));
|
||||
}
|
||||
|
||||
protected Object getValue() {
|
||||
String path = getConfigPath(); // 当未指定路径时,优先抛出异常
|
||||
return getConfiguration().get(path);
|
||||
}
|
||||
|
||||
protected void setValue(@Nullable Object value) {
|
||||
getConfiguration().set(getConfigPath(), value);
|
||||
}
|
||||
|
||||
public @Nullable String getInlineComment() {
|
||||
return inlineComment;
|
||||
}
|
||||
|
||||
@Unmodifiable
|
||||
public @Nullable List<String> getHeaderComments() {
|
||||
return headerComments;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value.impl;
|
||||
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class CachedConfigValue<T> extends ConfigValue<T> {
|
||||
|
||||
|
||||
protected @Nullable T cachedValue;
|
||||
protected long parsedTime = -1;
|
||||
|
||||
protected CachedConfigValue(@NotNull ValueManifest<T> manifest) {
|
||||
super(manifest);
|
||||
}
|
||||
|
||||
protected T updateCache(T value) {
|
||||
this.parsedTime = System.currentTimeMillis();
|
||||
this.cachedValue = value;
|
||||
return getCachedValue();
|
||||
}
|
||||
|
||||
public @Nullable T getCachedValue() {
|
||||
return cachedValue;
|
||||
}
|
||||
|
||||
public boolean isExpired() {
|
||||
return this.parsedTime <= 0 || getProvider().isExpired(this.parsedTime);
|
||||
}
|
||||
|
||||
protected final T getDefaultFirst(@Nullable T value) {
|
||||
return updateCache(this.defaultValue == null ? value : this.defaultValue);
|
||||
}
|
||||
|
||||
protected @Nullable T getCachedOrDefault() {
|
||||
return getCachedOrDefault(null);
|
||||
}
|
||||
|
||||
@Contract("!null->!null")
|
||||
protected T getCachedOrDefault(@Nullable T emptyValue) {
|
||||
if (getCachedValue() != null) return getCachedValue();
|
||||
else if (getDefaultValue() != null) return getDefaultValue();
|
||||
else return emptyValue;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements List<V> {
|
||||
|
||||
public static <V> @NotNull ConfigListBuilder<V> builderOf(@NotNull Class<V> valueClass) {
|
||||
return builder().asList(valueClass);
|
||||
}
|
||||
|
||||
public static <V> @NotNull ConfiguredList<V> of(@NotNull Class<V> valueClass, @NotNull Collection<V> defaults) {
|
||||
return builderOf(valueClass).fromObject().defaults(defaults).build();
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <V> @NotNull ConfiguredList<V> of(@NotNull Class<V> valueClass, @NotNull V... defaults) {
|
||||
return builderOf(valueClass).fromObject().defaults(defaults).build();
|
||||
}
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected final @NotNull ConfigDataFunction<Object, V> parser;
|
||||
protected final @NotNull ConfigDataFunction<V, Object> serializer;
|
||||
|
||||
public ConfiguredList(@NotNull ValueManifest<List<V>> manifest, @NotNull Class<V> valueClass,
|
||||
@NotNull ConfigDataFunction<Object, V> parser,
|
||||
@NotNull ConfigDataFunction<V, Object> serializer) {
|
||||
super(manifest);
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<V> get() {
|
||||
if (!isExpired()) return getCachedOrDefault(new ArrayList<>());
|
||||
|
||||
// 已过时的数据,需要重新解析一次。
|
||||
List<V> list = new ArrayList<>();
|
||||
List<?> data = getConfiguration().contains(getConfigPath()) ?
|
||||
getConfiguration().getList(getConfigPath()) : null;
|
||||
if (data == null) return getDefaultFirst(list);
|
||||
for (Object dataVal : data) {
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
list.add(parser.parse(dataVal));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return updateCache(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(int index) {
|
||||
return get().get(index);
|
||||
}
|
||||
|
||||
public <T> @NotNull T handle(Function<List<V>, T> function) {
|
||||
List<V> list = get();
|
||||
T result = function.apply(list);
|
||||
set(list);
|
||||
return result;
|
||||
}
|
||||
|
||||
public @NotNull List<V> modify(Consumer<List<V>> consumer) {
|
||||
List<V> list = get();
|
||||
consumer.accept(list);
|
||||
set(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@Nullable List<V> value) {
|
||||
updateCache(value);
|
||||
if (value == null) setValue(null);
|
||||
else {
|
||||
List<Object> data = new ArrayList<>();
|
||||
for (V val : value) {
|
||||
if (val == null) continue;
|
||||
try {
|
||||
data.add(serializer.parse(val));
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
setValue(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V set(int index, V element) {
|
||||
return handle(list -> list.set(index, element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return get().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return get().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return get().contains(o);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return get().iterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object @NotNull [] toArray() {
|
||||
return get().toArray();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> T @NotNull [] toArray(@NotNull T[] a) {
|
||||
return get().toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NotNull Collection<?> c) {
|
||||
return new HashSet<>(get()).containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(V v) {
|
||||
handle(list -> list.add(v));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, V element) {
|
||||
modify(list -> list.add(index, element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends V> c) {
|
||||
return handle(list -> list.addAll(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, @NotNull Collection<? extends V> c) {
|
||||
return handle(list -> list.addAll(index, c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return handle(list -> list.remove(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(int index) {
|
||||
return handle(list -> list.remove(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> c) {
|
||||
return handle(list -> list.removeAll(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> c) {
|
||||
return handle(list -> list.retainAll(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
modify(List::clear);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return get().indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return get().lastIndexOf(o);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<V> listIterator() {
|
||||
return get().listIterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<V> listIterator(int index) {
|
||||
return get().listIterator(index);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<V> subList(int fromIndex, int toIndex) {
|
||||
return get().subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.map.ConfigMapCreator;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements Map<K, V> {
|
||||
|
||||
public static <K, V> @NotNull ConfigMapCreator<K, V> builderOf(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return builder().asMap(keyClass, valueClass);
|
||||
}
|
||||
|
||||
protected final @NotNull Supplier<? extends Map<K, V>> supplier;
|
||||
|
||||
protected final @NotNull Class<K> keyClass;
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected final @NotNull ConfigDataFunction<String, K> keyParser;
|
||||
protected final @NotNull ConfigDataFunction<Object, V> valueParser;
|
||||
|
||||
protected final @NotNull ConfigDataFunction<K, String> keySerializer;
|
||||
protected final @NotNull ConfigDataFunction<V, Object> valueSerializer;
|
||||
|
||||
|
||||
public ConfiguredMap(@NotNull ValueManifest<Map<K, V>> manifest,
|
||||
@NotNull Supplier<? extends Map<K, V>> mapObjSupplier,
|
||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<Object, V> valueParser,
|
||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
||||
@NotNull ConfigDataFunction<V, Object> valueSerializer) {
|
||||
super(manifest);
|
||||
this.supplier = mapObjSupplier;
|
||||
this.keyClass = keyClass;
|
||||
this.valueClass = valueClass;
|
||||
this.keyParser = keyParser;
|
||||
this.valueParser = valueParser;
|
||||
this.keySerializer = keySerializer;
|
||||
this.valueSerializer = valueSerializer;
|
||||
}
|
||||
|
||||
public @NotNull Class<K> getKeyClass() {
|
||||
return keyClass;
|
||||
}
|
||||
|
||||
public @NotNull Class<V> getValueClass() {
|
||||
return valueClass;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<String, K> getKeyParser() {
|
||||
return keyParser;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<Object, V> getValueParser() {
|
||||
return valueParser;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<K, String> getKeySerializer() {
|
||||
return keySerializer;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<V, Object> getValueSerializer() {
|
||||
return valueSerializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<K, V> get() {
|
||||
if (!isExpired()) return getCachedOrDefault(supplier.get());
|
||||
|
||||
// 已过时的数据,需要重新解析一次。
|
||||
Map<K, V> map = supplier.get();
|
||||
|
||||
ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath());
|
||||
if (section == null) return getDefaultFirst(map);
|
||||
|
||||
Set<String> keys = section.getKeys(false);
|
||||
if (keys.isEmpty()) return getDefaultFirst(map);
|
||||
|
||||
for (String dataKey : keys) {
|
||||
Object dataVal = section.get(dataKey);
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
K key = keyParser.parse(dataKey);
|
||||
V value = valueParser.parse(dataVal);
|
||||
map.put(key, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return updateCache(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return get().get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@Nullable Map<K, V> value) {
|
||||
updateCache(value);
|
||||
if (value == null) setValue(null);
|
||||
else {
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
for (Map.Entry<K, V> entry : value.entrySet()) {
|
||||
try {
|
||||
data.put(
|
||||
keySerializer.parse(entry.getKey()),
|
||||
valueSerializer.parse(entry.getValue())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
setValue(data);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> @NotNull T modifyValue(Function<Map<K, V>, T> function) {
|
||||
Map<K, V> m = get();
|
||||
T result = function.apply(m);
|
||||
set(m);
|
||||
return result;
|
||||
}
|
||||
|
||||
public @NotNull Map<K, V> modifyMap(Consumer<Map<K, V>> consumer) {
|
||||
Map<K, V> m = get();
|
||||
consumer.accept(m);
|
||||
set(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return get().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return get().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return get().containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return get().containsValue(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
return modifyValue(m -> m.put(key, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
return modifyValue(m -> m.remove(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
|
||||
modifyMap(map -> map.putAll(m));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
modifyMap(Map::clear);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return get().keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return get().values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@Unmodifiable
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return get().entrySet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.value.SectionValueBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfiguredSection<V> extends CachedConfigValue<V> {
|
||||
|
||||
public static <V> @NotNull SectionValueBuilder<V> builderOf(@NotNull Class<V> valueClass) {
|
||||
return builder().asValue(valueClass).fromSection();
|
||||
}
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected final @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser;
|
||||
protected final @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
|
||||
|
||||
public ConfiguredSection(@NotNull ValueManifest<V> manifest, @NotNull Class<V> valueClass,
|
||||
@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser,
|
||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
||||
super(manifest);
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public @NotNull Class<V> getValueClass() {
|
||||
return valueClass;
|
||||
}
|
||||
|
||||
public @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> getSerializer() {
|
||||
return serializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable V get() {
|
||||
if (!isExpired()) return getCachedOrDefault();
|
||||
// 已过时的数据,需要重新解析一次。
|
||||
|
||||
ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath());
|
||||
if (section == null) return getDefaultValue();
|
||||
|
||||
try {
|
||||
// 若未出现错误,则直接更新缓存并返回。
|
||||
return updateCache(this.parser.parse(section, this.defaultValue));
|
||||
} catch (Exception e) {
|
||||
// 出现了解析错误,提示并返回默认值。
|
||||
e.printStackTrace();
|
||||
return getDefaultValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(V value) {
|
||||
updateCache(value);
|
||||
if (value == null) setValue(null);
|
||||
else {
|
||||
try {
|
||||
setValue(serializer.parse(value));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.function.ConfigValueParser;
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ConfiguredValue<V> extends CachedConfigValue<V> {
|
||||
|
||||
public static <V> ConfigValueBuilder<V> builderOf(Class<V> valueClass) {
|
||||
return builder().asValue(valueClass);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(Class<V> valueClass) {
|
||||
return of(valueClass, null);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(Class<V> valueClass, @Nullable V defaultValue) {
|
||||
return builderOf(valueClass).fromObject().defaults(defaultValue).build();
|
||||
}
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected final @NotNull ConfigValueParser<Object, V> parser;
|
||||
protected final @NotNull ConfigDataFunction<V, Object> serializer;
|
||||
|
||||
public ConfiguredValue(@NotNull ValueManifest<V> manifest, @NotNull Class<V> valueClass,
|
||||
@NotNull ConfigValueParser<Object, V> parser,
|
||||
@NotNull ConfigDataFunction<V, Object> serializer) {
|
||||
super(manifest);
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public @NotNull Class<V> getValueClass() {
|
||||
return valueClass;
|
||||
}
|
||||
|
||||
public @NotNull ConfigValueParser<Object, V> getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get() {
|
||||
if (!isExpired()) return getCachedOrDefault();
|
||||
// 已过时的数据,需要重新解析一次。
|
||||
|
||||
Object value = getValue();
|
||||
if (value == null) return getDefaultValue(); // 获取的值不存在,直接使用默认值。
|
||||
try {
|
||||
// 若未出现错误,则直接更新缓存并返回。
|
||||
return updateCache(this.parser.parse(value, this.defaultValue));
|
||||
} catch (Exception e) {
|
||||
// 出现了解析错误,提示并返回默认值。
|
||||
e.printStackTrace();
|
||||
return getDefaultValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(V value) {
|
||||
updateCache(value);
|
||||
if (value == null) setValue(null);
|
||||
else {
|
||||
try {
|
||||
setValue(serializer.parse(value));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface DataConsumer<T> {
|
||||
|
||||
void accept(@NotNull T data) throws Exception;
|
||||
|
||||
default DataConsumer<T> andThen(DataConsumer<? super T> after) {
|
||||
return (T t) -> {
|
||||
accept(t);
|
||||
after.accept(t);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+37
-36
@@ -1,4 +1,4 @@
|
||||
package cc.carm.lib.configuration.core.function;
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
@@ -7,72 +7,73 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Objects;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigDataFunction<T, R> {
|
||||
public interface DataFunction<T, R> {
|
||||
|
||||
@NotNull R parse(@NotNull T data) throws Exception;
|
||||
@NotNull R handle(@NotNull T data) throws Exception;
|
||||
|
||||
default <V> @NotNull ConfigDataFunction<T, V> andThen(@NotNull ConfigDataFunction<? super R, V> after) {
|
||||
default <V> @NotNull DataFunction<T, V> andThen(@NotNull DataFunction<? super R, V> after) {
|
||||
Objects.requireNonNull(after);
|
||||
return ((data) -> after.parse(parse(data)));
|
||||
return data -> after.handle(handle(data));
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, T> identity() {
|
||||
return (input) -> input;
|
||||
static <T> @NotNull DataFunction<T, T> identity() {
|
||||
return input -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, T> identity(Class<T> type) {
|
||||
return (input) -> input;
|
||||
static <T> @NotNull DataFunction<T, T> identity(Class<T> type) {
|
||||
return input -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull ConfigDataFunction<T, V> required() {
|
||||
return (input) -> {
|
||||
static <T, V> @NotNull DataFunction<T, V> required() {
|
||||
return input -> {
|
||||
throw new IllegalArgumentException("Please specify the value parser.");
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, Object> toObject() {
|
||||
return (input) -> input;
|
||||
static <T> @NotNull DataFunction<T, Object> toObject() {
|
||||
return input -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigDataFunction<Object, V> castObject(Class<V> valueClass) {
|
||||
return (input) -> {
|
||||
static <V> @NotNull DataFunction<Object, V> castObject(Class<V> valueClass) {
|
||||
return input -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigDataFunction<String, V> castFromString(Class<V> valueClass) {
|
||||
return (input) -> {
|
||||
static <V> @NotNull DataFunction<String, V> castFromString(Class<V> valueClass) {
|
||||
return input -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, String> castToString() {
|
||||
return (input) -> {
|
||||
static <T> @NotNull DataFunction<T, String> castToString() {
|
||||
return input -> {
|
||||
if (input instanceof String) return (String) input;
|
||||
else if (input instanceof Enum<?>) return ((Enum<?>) input).name();
|
||||
else return input.toString();
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigDataFunction<String, V> parseString(Class<V> valueClass) {
|
||||
return (input) -> {
|
||||
static <V> @NotNull DataFunction<String, V> parseString(Class<V> valueClass) {
|
||||
return input -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Integer> intValue() {
|
||||
return (input) -> {
|
||||
static @NotNull DataFunction<Object, Integer> intValue() {
|
||||
return input -> {
|
||||
if (input instanceof Integer) {
|
||||
return (Integer) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -82,8 +83,8 @@ public interface ConfigDataFunction<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Short> shortValue() {
|
||||
return (input) -> {
|
||||
static @NotNull DataFunction<Object, Short> shortValue() {
|
||||
return input -> {
|
||||
if (input instanceof Short) {
|
||||
return (Short) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -93,8 +94,8 @@ public interface ConfigDataFunction<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Double> doubleValue() {
|
||||
return (input) -> {
|
||||
static @NotNull DataFunction<Object, Double> doubleValue() {
|
||||
return input -> {
|
||||
if (input instanceof Double) {
|
||||
return (Double) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -104,8 +105,8 @@ public interface ConfigDataFunction<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Byte> byteValue() {
|
||||
return (input) -> {
|
||||
static @NotNull DataFunction<Object, Byte> byteValue() {
|
||||
return input -> {
|
||||
if (input instanceof Byte) {
|
||||
return (Byte) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -115,8 +116,8 @@ public interface ConfigDataFunction<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Float> floatValue() {
|
||||
return (input) -> {
|
||||
static @NotNull DataFunction<Object, Float> floatValue() {
|
||||
return input -> {
|
||||
if (input instanceof Float) {
|
||||
return (Float) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -126,8 +127,8 @@ public interface ConfigDataFunction<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Long> longValue() {
|
||||
return (input) -> {
|
||||
static @NotNull DataFunction<Object, Long> longValue() {
|
||||
return input -> {
|
||||
if (input instanceof Long) {
|
||||
return (Long) input;
|
||||
} else if (input instanceof Number) {
|
||||
@@ -137,15 +138,15 @@ public interface ConfigDataFunction<T, R> {
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Boolean> booleanValue() {
|
||||
return (input) -> {
|
||||
static @NotNull DataFunction<Object, Boolean> booleanValue() {
|
||||
return input -> {
|
||||
if (input instanceof Boolean) {
|
||||
return (Boolean) input;
|
||||
} else if (input instanceof String) {
|
||||
String s = (String) input;
|
||||
return Boolean.parseBoolean(s) || "yes".equalsIgnoreCase(s);
|
||||
} else if (input instanceof Integer) {
|
||||
return ((Integer) input) == 1;
|
||||
return (Integer) input == 1;
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Boolean.class.getName());
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package cc.carm.lib.configuration.function;
|
||||
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ValueHandler<T, R> {
|
||||
|
||||
@Nullable R handle(@NotNull ConfigurationHolder<?> holder, @NotNull T data) throws Exception;
|
||||
|
||||
default <V> ValueHandler<T, V> andThen(@NotNull ValueHandler<R, V> after) {
|
||||
Objects.requireNonNull(after);
|
||||
return ((provider, data) -> {
|
||||
R result = handle(provider, data);
|
||||
if (result == null) return null;
|
||||
else return after.handle(provider, result);
|
||||
});
|
||||
}
|
||||
|
||||
default <V> ValueHandler<V, R> compose(@NotNull ValueHandler<? super V, ? extends T> before) {
|
||||
Objects.requireNonNull(before);
|
||||
return ((provider, data) -> {
|
||||
T result = before.handle(provider, data);
|
||||
if (result == null) return null;
|
||||
return handle(provider, result);
|
||||
});
|
||||
}
|
||||
|
||||
default <V> ValueHandler<V, R> compose(@NotNull DataFunction<? super V, ? extends T> before) {
|
||||
Objects.requireNonNull(before);
|
||||
return ((provider, data) -> {
|
||||
T result = before.handle(data);
|
||||
return handle(provider, result);
|
||||
});
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ValueHandler<T, T> identity() {
|
||||
return (provider, input) -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ValueHandler<T, Object> toObject() {
|
||||
return ConfigurationHolder::serialize;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ValueHandler<T, String> stringValue() {
|
||||
return (provider, input) -> String.valueOf(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <O, T> @NotNull ValueHandler<O, T> deserialize(ValueType<T> to) {
|
||||
return (provider, input) -> provider.deserialize(to, input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull ValueHandler<T, V> required() {
|
||||
return (provider, input) -> {
|
||||
throw new IllegalArgumentException("Please specify the value parser.");
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull ValueHandler<T, V> required(ValueType<V> type) {
|
||||
return (provider, input) -> {
|
||||
if (type.isInstance(input)) return type.cast(input); // Simple cast
|
||||
throw new IllegalArgumentException("Please specify the value parser.");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
package cc.carm.lib.configuration.source;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.*;
|
||||
import cc.carm.lib.configuration.adapter.strandard.StandardAdapters;
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||
import cc.carm.lib.configuration.source.loader.PathGenerator;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
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<
|
||||
SOURCE extends ConfigureSource<?, ?, SOURCE>,
|
||||
HOLDER extends ConfigurationHolder<SOURCE>,
|
||||
SELF
|
||||
> {
|
||||
|
||||
protected ValueAdapterRegistry adapters = new ValueAdapterRegistry();
|
||||
protected ConfigurationOptionHolder options = new ConfigurationOptionHolder();
|
||||
protected @NotNull Map<String, ConfigurationMetaHolder> metadata = new HashMap<>();
|
||||
protected ConfigurationInitializer initializer = new ConfigurationInitializer();
|
||||
|
||||
public ConfigurationFactory() {
|
||||
this.adapters.register(StandardAdapters.PRIMITIVES);
|
||||
this.adapters.register(StandardAdapters.SECTIONS);
|
||||
this.adapters.register(StandardAdapters.ENUMS);
|
||||
}
|
||||
|
||||
protected abstract SELF self();
|
||||
|
||||
public SELF adapters(ValueAdapterRegistry adapters) {
|
||||
this.adapters = adapters;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF adapter(Consumer<ValueAdapterRegistry> adapterRegistryConsumer) {
|
||||
adapterRegistryConsumer.accept(adapters);
|
||||
return self();
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueAdapter<T> adapter) {
|
||||
return adapter(a -> a.register(adapter));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer) {
|
||||
return adapter(a -> a.register(type, serializer));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueParser<T> parser) {
|
||||
return adapter(a -> a.register(type, parser));
|
||||
}
|
||||
|
||||
public <FROM, TO> SELF adapter(@NotNull Class<FROM> from, @NotNull Class<TO> to,
|
||||
@NotNull DataFunction<FROM, TO> parser,
|
||||
@NotNull DataFunction<TO, FROM> serializer) {
|
||||
return adapter(a -> a.register(from, to, parser, serializer));
|
||||
}
|
||||
|
||||
public <FROM, TO> SELF adapter(@NotNull ValueType<FROM> from, @NotNull ValueType<TO> to,
|
||||
@NotNull DataFunction<FROM, TO> parser,
|
||||
@NotNull DataFunction<TO, FROM> serializer) {
|
||||
return adapter(a -> a.register(from, to, parser, serializer));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull ValueType<T> type, @NotNull ValueSerializer<T> serializer, @NotNull ValueParser<T> parser) {
|
||||
return adapter(a -> a.register(type, serializer, parser));
|
||||
}
|
||||
|
||||
public <T> SELF adapter(@NotNull Class<T> type, @NotNull ValueSerializer<T> serializer, @NotNull ValueParser<T> parser) {
|
||||
return adapter(ValueType.of(type), serializer, parser);
|
||||
}
|
||||
|
||||
public SELF options(ConfigurationOptionHolder options) {
|
||||
this.options = options;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF option(Consumer<ConfigurationOptionHolder> modifier) {
|
||||
modifier.accept(options);
|
||||
return self();
|
||||
}
|
||||
|
||||
public <O> SELF option(ConfigurationOption<O> type, O value) {
|
||||
return option(o -> o.set(type, value));
|
||||
}
|
||||
|
||||
public <O> SELF option(ConfigurationOption<O> type, Supplier<O> value) {
|
||||
return option(type, value.get());
|
||||
}
|
||||
|
||||
public <O> SELF option(ConfigurationOption<O> type, Consumer<O> modifier) {
|
||||
return option(holder -> {
|
||||
O current = holder.get(type);
|
||||
modifier.accept(current);
|
||||
holder.set(type, current);
|
||||
});
|
||||
}
|
||||
|
||||
public SELF metadata(@NotNull Map<String, ConfigurationMetaHolder> metadata) {
|
||||
this.metadata = metadata;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF metadata(@NotNull Consumer<Map<String, ConfigurationMetaHolder>> handler) {
|
||||
handler.accept(this.metadata);
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF metadata(@Nullable String path, @NotNull ConfigurationMetaHolder meta) {
|
||||
return metadata(m -> m.put(path, meta));
|
||||
}
|
||||
|
||||
public SELF metadata(@Nullable String path, @NotNull Consumer<ConfigurationMetaHolder> handler) {
|
||||
return metadata(map -> {
|
||||
ConfigurationMetaHolder meta = map.computeIfAbsent(path, k -> new ConfigurationMetaHolder());
|
||||
handler.accept(meta);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public SELF initializer(ConfigurationInitializer initializer) {
|
||||
this.initializer = initializer;
|
||||
return self();
|
||||
}
|
||||
|
||||
public SELF initializer(Consumer<ConfigurationInitializer> initializerConsumer) {
|
||||
initializerConsumer.accept(initializer);
|
||||
return self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Supply the base path generator for this configuration holder
|
||||
*
|
||||
* @param generator {@link PathGenerator}
|
||||
* @return this
|
||||
*/
|
||||
public SELF pathGenerator(PathGenerator generator) {
|
||||
return initializer(loader -> {
|
||||
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,
|
||||
@NotNull ConfigurationMetadata<M> metadata,
|
||||
@NotNull Function<A, M> extractor) {
|
||||
return initializer(loader -> loader.registerAnnotation(annotation, metadata, extractor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the configuration holder.
|
||||
*
|
||||
* @return The configuration holder
|
||||
*/
|
||||
public abstract @NotNull HOLDER build();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package cc.carm.lib.configuration.source;
|
||||
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
|
||||
import cc.carm.lib.configuration.source.option.ConfigurationOption;
|
||||
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnmodifiableView;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, SOURCE>> {
|
||||
|
||||
protected final @NotNull ValueAdapterRegistry adapters;
|
||||
protected final @NotNull ConfigurationOptionHolder options;
|
||||
protected final @NotNull Map<String, ConfigurationMetaHolder> metadata;
|
||||
|
||||
protected final @NotNull ConfigurationInitializer initializer;
|
||||
|
||||
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
|
||||
@NotNull ConfigurationOptionHolder options,
|
||||
@NotNull Map<String, ConfigurationMetaHolder> metadata,
|
||||
@NotNull ConfigurationInitializer initializer) {
|
||||
this.initializer = initializer;
|
||||
this.adapters = adapters;
|
||||
this.options = options;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public abstract @NotNull SOURCE config();
|
||||
|
||||
public void reload() throws Exception {
|
||||
config().reload();
|
||||
}
|
||||
|
||||
public void save() throws Exception {
|
||||
config().save();
|
||||
}
|
||||
|
||||
public ConfigurationOptionHolder options() {
|
||||
return options;
|
||||
}
|
||||
|
||||
public <O> O option(@NotNull ConfigurationOption<O> option) {
|
||||
return options().get(option);
|
||||
}
|
||||
|
||||
public @NotNull Map<String, ConfigurationMetaHolder> metadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationMetaHolder metadata(@Nullable String path) {
|
||||
return metadata().computeIfAbsent(path, k -> new ConfigurationMetaHolder());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@UnmodifiableView
|
||||
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type) {
|
||||
Map<String, M> metas = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, ConfigurationMetaHolder> entry : this.metadata.entrySet()) {
|
||||
M data = entry.getValue().get(type);
|
||||
if (data != null) metas.put(entry.getKey(), data);
|
||||
}
|
||||
return Collections.unmodifiableMap(metas);
|
||||
}
|
||||
|
||||
public ValueAdapterRegistry adapters() {
|
||||
return this.adapters;
|
||||
}
|
||||
|
||||
public ConfigurationInitializer initializer() {
|
||||
return initializer;
|
||||
}
|
||||
|
||||
@Contract("_,null -> null")
|
||||
public <T> T deserialize(@NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||
return adapters().deserialize(this, type, source);
|
||||
}
|
||||
|
||||
@Contract("_,null -> null")
|
||||
public <T> T deserialize(@NotNull ValueType<T> type, @Nullable Object source) throws Exception {
|
||||
return adapters().deserialize(this, type, source);
|
||||
}
|
||||
|
||||
@Contract("null -> null")
|
||||
public <T> Object serialize(@Nullable T value) throws Exception {
|
||||
return adapters().serialize(this, value);
|
||||
}
|
||||
|
||||
public void initialize(Class<? extends Configuration> configClass) {
|
||||
try {
|
||||
initializer.initialize(this, configClass);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize(@NotNull Configuration config) {
|
||||
try {
|
||||
initializer.initialize(this, config);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ValueManifest<?> value) {
|
||||
value.holder(this);
|
||||
}
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package cc.carm.lib.configuration.source.loader;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ConfigInitializeHandler<T> {
|
||||
|
||||
static <T> ConfigInitializeHandler<T> start() {
|
||||
return (provider, path, value) -> {
|
||||
};
|
||||
}
|
||||
|
||||
void whenInitialize(@NotNull ConfigurationHolder<?> holder, @Nullable String path, @NotNull T value) throws Exception;
|
||||
|
||||
default ConfigInitializeHandler<T> andThen(ConfigInitializeHandler<T> after) {
|
||||
return (provider, path, value) -> {
|
||||
whenInitialize(provider, path, value);
|
||||
after.whenInitialize(provider, path, value);
|
||||
};
|
||||
}
|
||||
|
||||
default ConfigInitializeHandler<T> compose(ConfigInitializeHandler<T> before) {
|
||||
return (provider, path, value) -> {
|
||||
before.whenInitialize(provider, path, value);
|
||||
whenInitialize(provider, path, value);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
package cc.carm.lib.configuration.source.loader;
|
||||
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
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.value.ConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Configuration initializer,
|
||||
* used to initialize {@link ConfigValue}s from {@link Configuration} classes.
|
||||
*/
|
||||
public class ConfigurationInitializer {
|
||||
|
||||
protected @NotNull PathGenerator pathGenerator;
|
||||
protected @NotNull ConfigInitializeHandler<Field> fieldInitializer;
|
||||
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer;
|
||||
|
||||
public ConfigurationInitializer() {
|
||||
this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start());
|
||||
}
|
||||
|
||||
public ConfigurationInitializer(@NotNull PathGenerator pathGenerator,
|
||||
@NotNull ConfigInitializeHandler<Field> fieldInitializer,
|
||||
@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
|
||||
this.pathGenerator = pathGenerator;
|
||||
this.fieldInitializer = fieldInitializer;
|
||||
this.classInitializer = classInitializer;
|
||||
}
|
||||
|
||||
public void pathGenerator(@NotNull PathGenerator pathGenerator) {
|
||||
this.pathGenerator = pathGenerator;
|
||||
}
|
||||
|
||||
public @NotNull PathGenerator pathGenerator() {
|
||||
return pathGenerator;
|
||||
}
|
||||
|
||||
public ConfigInitializeHandler<Field> fieldInitializer() {
|
||||
return fieldInitializer;
|
||||
}
|
||||
|
||||
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
|
||||
this.fieldInitializer = fieldInitializer;
|
||||
}
|
||||
|
||||
public ConfigInitializeHandler<Class<? extends Configuration>> classInitializer() {
|
||||
return classInitializer;
|
||||
}
|
||||
|
||||
public void classInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
|
||||
this.classInitializer = classInitializer;
|
||||
}
|
||||
|
||||
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
|
||||
this.fieldInitializer = this.fieldInitializer.andThen(fieldInitializer);
|
||||
}
|
||||
|
||||
public void appendClassInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
|
||||
this.classInitializer = this.classInitializer.andThen(classInitializer);
|
||||
}
|
||||
|
||||
public <T, A extends Annotation> void registerClassAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull ConfigurationMetadata<T> metadata,
|
||||
@NotNull Function<A, T> extractor) {
|
||||
appendClassInitializer((holder, path, clazz) -> {
|
||||
A data = clazz.getAnnotation(annotation);
|
||||
if (data == null) return;
|
||||
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||
});
|
||||
}
|
||||
|
||||
public <T, A extends Annotation> void registerFieldAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull ConfigurationMetadata<T> metadata,
|
||||
@NotNull Function<A, T> extractor) {
|
||||
appendFieldInitializer((holder, path, field) -> {
|
||||
A data = field.getAnnotation(annotation);
|
||||
if (data == null) return;
|
||||
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
|
||||
});
|
||||
}
|
||||
|
||||
public <T, A extends Annotation> void registerAnnotation(@NotNull Class<A> annotation,
|
||||
@NotNull ConfigurationMetadata<T> metadata,
|
||||
@NotNull Function<A, T> extractor) {
|
||||
registerClassAnnotation(annotation, metadata, extractor);
|
||||
registerFieldAnnotation(annotation, metadata, extractor);
|
||||
}
|
||||
|
||||
|
||||
public @Nullable String getFieldPath(@NotNull ConfigurationHolder<?> holder, @Nullable String parentPath, @NotNull Field field) {
|
||||
return pathGenerator.getFieldPath(holder, parentPath, field);
|
||||
}
|
||||
|
||||
public @Nullable String getClassPath(@NotNull ConfigurationHolder<?> holder, @Nullable String parentPath,
|
||||
@NotNull Class<?> clazz, @Nullable Field clazzField) {
|
||||
return pathGenerator.getClassPath(holder, parentPath, clazz, clazzField);
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull Configuration config) throws Exception {
|
||||
initializeInstance(holder, config, null, null);
|
||||
if (holder.option(StandardOptions.SET_DEFAULTS)) holder.save();
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull Class<? extends Configuration> clazz) throws Exception {
|
||||
initializeStaticClass(holder, clazz, null, null);
|
||||
if (holder.option(StandardOptions.SET_DEFAULTS)) holder.save();
|
||||
}
|
||||
|
||||
|
||||
// 针对实例类的初始化方法
|
||||
protected void initializeInstance(@NotNull ConfigurationHolder<?> holder, @NotNull Configuration root,
|
||||
@Nullable String parentPath, @Nullable Field configField) {
|
||||
String path = getClassPath(holder, parentPath, root.getClass(), configField);
|
||||
try {
|
||||
this.classInitializer.whenInitialize(holder, path, root.getClass());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(holder, root, field, path));
|
||||
}
|
||||
|
||||
// 针对静态类的初始化方法
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void initializeStaticClass(@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull Class<?> clazz,
|
||||
@Nullable String parentPath, @Nullable Field configField) {
|
||||
if (!Configuration.class.isAssignableFrom(clazz)) return; // Only Configuration class can be initialized.
|
||||
|
||||
String path = getClassPath(holder, parentPath, clazz, configField);
|
||||
|
||||
try {
|
||||
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
initializeField(holder, clazz, field, path);
|
||||
}
|
||||
|
||||
if (holder.option(StandardOptions.LOAD_SUB_CLASSES)) {
|
||||
Class<?>[] classes = clazz.getDeclaredClasses();
|
||||
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
|
||||
initializeStaticClass(holder, classes[i], path, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void initializeField(@NotNull ConfigurationHolder<?> holder,
|
||||
@NotNull Object source, @NotNull Field field, @Nullable String parent) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
Object object = field.get(source);
|
||||
//
|
||||
if (object instanceof ConfigValue<?>) {
|
||||
// 目标是 ConfigValue 实例,进行具体的初始化注入
|
||||
ConfigValue<?> value = (ConfigValue<?>) object;
|
||||
String path = getFieldPath(holder, parent, field);
|
||||
if (path == null) return;
|
||||
value.initialize(holder, path);
|
||||
holder.metadata(path).set(StandardMeta.UNIT, true); // Mark the minimal config value unit.
|
||||
try {
|
||||
this.fieldInitializer.whenInitialize(holder, path, field);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (holder.option(StandardOptions.SET_DEFAULTS)) {
|
||||
value.setDefault(); // Set default value.
|
||||
}
|
||||
if (holder.option(StandardOptions.PRELOAD)) {
|
||||
value.get(); // Preload the value by calling #get method.
|
||||
}
|
||||
} else if (source instanceof Configuration && object instanceof Configuration) {
|
||||
// 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。
|
||||
initializeInstance(holder, (Configuration) object, parent, field);
|
||||
} else if (source instanceof Class<?> && object instanceof Class<?>) {
|
||||
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
|
||||
initializeStaticClass(holder, (Class<?>) object, parent, field);
|
||||
}
|
||||
|
||||
// 以上判断实现以下规范:
|
||||
// - 实例类中仅加载 ConfigValue实例 与 Configuration实例
|
||||
// - 静态类中仅加载 静态ConfigValue实例 与 静态Configuration类
|
||||
|
||||
} catch (IllegalAccessException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package cc.carm.lib.configuration.source.loader;
|
||||
|
||||
import cc.carm.lib.configuration.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
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 static PathGenerator of() {
|
||||
return of(PathGenerator::covertPathName);
|
||||
}
|
||||
|
||||
public static PathGenerator of(UnaryOperator<String> pathConverter) {
|
||||
return new PathGenerator(pathConverter);
|
||||
}
|
||||
|
||||
protected UnaryOperator<String> pathConverter;
|
||||
|
||||
public PathGenerator(UnaryOperator<String> pathConverter) {
|
||||
this.pathConverter = pathConverter;
|
||||
}
|
||||
|
||||
public @NotNull UnaryOperator<String> getPathConverter() {
|
||||
return pathConverter;
|
||||
}
|
||||
|
||||
public void setPathConverter(UnaryOperator<String> pathConverter) {
|
||||
this.pathConverter = pathConverter;
|
||||
}
|
||||
|
||||
public String covertPath(String name) {
|
||||
return pathConverter.apply(name);
|
||||
}
|
||||
|
||||
public @Nullable String getFieldPath(@NotNull ConfigurationHolder<?> holder,
|
||||
@Nullable String parentPath, @NotNull Field field) {
|
||||
ConfigPath path = field.getAnnotation(ConfigPath.class);
|
||||
if (path == null)
|
||||
return link(holder, parentPath, false, covertPath(field.getName())); // No annotation, use field name.
|
||||
else return link(holder, parentPath, path.root(), select(path.value(), covertPath(field.getName())));
|
||||
}
|
||||
|
||||
public @Nullable String getClassPath(@NotNull ConfigurationHolder<?> holder,
|
||||
@Nullable String parentPath, @NotNull Class<?> clazz, @Nullable Field clazzField) {
|
||||
// For standard path generator, we generate path following by:
|
||||
// 1. Check if the class has a ConfigPath annotation, if so, use the root and value as the path.
|
||||
// 2. If the class defined as a field, check if the field has a ConfigPath annotation,
|
||||
// and use filed information.
|
||||
ConfigPath clazzPath = clazz.getAnnotation(ConfigPath.class);
|
||||
if (clazzPath != null) return link(holder, parentPath, clazzPath.root(), clazzPath.value());
|
||||
|
||||
if (clazzField != null) {
|
||||
ConfigPath fieldPath = clazzField.getAnnotation(ConfigPath.class);
|
||||
if (fieldPath == null) return link(holder, parentPath, false, covertPath(clazzField.getName()));
|
||||
else return getFieldPath(holder, parentPath, clazzField);
|
||||
}
|
||||
|
||||
return link(holder, parentPath, false, covertPath(clazz.getSimpleName())); // No field, use class name.
|
||||
|
||||
}
|
||||
|
||||
protected String select(String path, String defaultValue) {
|
||||
if (path == null || path.isEmpty()) return defaultValue;
|
||||
else return isBlank(path) ? null : path;
|
||||
}
|
||||
|
||||
protected @Nullable String link(@NotNull ConfigurationHolder<?> holder,
|
||||
@Nullable String parent, boolean root, @Nullable String path) {
|
||||
if (path == null || path.isEmpty()) return root ? null : parent;
|
||||
return (root || parent == null) ? path : (parent + pathSeparator(holder) + path);
|
||||
}
|
||||
|
||||
public static boolean isBlank(String path) {
|
||||
return path == null || path.replace(" ", "").isEmpty();
|
||||
}
|
||||
|
||||
public static char pathSeparator(ConfigurationHolder<?> holder) {
|
||||
return holder.option(StandardOptions.PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration name of the specified element.
|
||||
* Use the naming convention of all lowercase and "-" links.
|
||||
* <p>
|
||||
* e.g. "SOME_NAME" will be "some-name"
|
||||
*
|
||||
* @param name source name
|
||||
* @return the final path
|
||||
*/
|
||||
public static String covertPathName(String name) {
|
||||
return name
|
||||
// Replace all uppercase letters with dashes
|
||||
.replaceAll("[A-Z]", "=$0")
|
||||
// If the first letter is also capitalized,
|
||||
// it will also be converted and the first dash will need to be removed
|
||||
.replaceAll("^=(.*)$", "$1")
|
||||
// Because the name may contain _, it needs to be treated a little differently
|
||||
.replaceAll("_=([A-Z])", "_$1")
|
||||
// The content that is not named in all caps is then converted
|
||||
.replaceAll("([a-z])=([A-Z])", "$1_$2")
|
||||
// Remove any extra horizontal lines
|
||||
.replace("=", "")
|
||||
// Replace the underscore with a dash
|
||||
.replace("_", "-")
|
||||
// Finally, convert it to all lowercase
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package cc.carm.lib.configuration.source.meta;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigurationMetaHolder {
|
||||
|
||||
protected final @NotNull Map<ConfigurationMetadata<?>, Object> values;
|
||||
|
||||
public ConfigurationMetaHolder() {
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
public ConfigurationMetaHolder(@NotNull Map<ConfigurationMetadata<?>, Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
public @NotNull Map<ConfigurationMetadata<?>, Object> values() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param defaultValue Default value if the value of option is not set.
|
||||
* @param <V> Value type
|
||||
* @return Value of option
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Contract("_,!null -> !null")
|
||||
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type, @Nullable V defaultValue) {
|
||||
return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param defaultValue Default value if the value of option is not set.
|
||||
* @param <V> Value type
|
||||
* @return Value of option
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Contract("_,!null -> !null")
|
||||
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type, Supplier<@Nullable V> defaultValue) {
|
||||
return (V) values().getOrDefault(type, type.defaultOrSupply(defaultValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param <V> Value type
|
||||
* @return Value of option
|
||||
*/
|
||||
public <V> @Nullable V get(@NotNull ConfigurationMetadata<V> type) {
|
||||
return get(type, (V) null);
|
||||
}
|
||||
|
||||
public boolean contains(@NotNull ConfigurationMetadata<?> type) {
|
||||
return values().containsKey(type) || type.hasDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of meta, if the value is null, the meta will be removed.
|
||||
* <br> Will only be changed in current holder.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param value Value of meta
|
||||
* @param <V> Value type
|
||||
* @return Previous value of meta
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> @Nullable V set(@NotNull ConfigurationMetadata<V> type, @Nullable V value) {
|
||||
if (value == null || type.isDefault(value)) {
|
||||
return (V) values().remove(type);
|
||||
} else {
|
||||
return (V) values().put(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of meta, if the value is null, the meta will not be changed.
|
||||
* <br> Will only be changed in current holder.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param value Value of meta
|
||||
* @param <V> Value type
|
||||
*/
|
||||
public <V> void setIfAbsent(@NotNull ConfigurationMetadata<V> type, @Nullable V value) {
|
||||
if (value == null || type.isDefault(value)) {
|
||||
values().remove(type);
|
||||
} else {
|
||||
values().putIfAbsent(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of meta, if the value is null, the meta will not be changed.
|
||||
* <br> Will only be changed in current holder.
|
||||
*
|
||||
* @param type {@link ConfigurationMetadata}
|
||||
* @param value Value of meta
|
||||
* @param <V> Value type
|
||||
* @return Previous value of meta
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> @Nullable V setIfPresent(@NotNull ConfigurationMetadata<V> type, @Nullable V value) {
|
||||
Object exists = values().get(type);
|
||||
if (exists == null) return null;
|
||||
|
||||
if (value == null || type.isDefault(value)) {
|
||||
return (V) values().remove(type);
|
||||
} else {
|
||||
return (V) values().put(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package cc.carm.lib.configuration.source.meta;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigurationMetadata<T> {
|
||||
|
||||
public static <T> ConfigurationMetadata<T> of() {
|
||||
return of(() -> null);
|
||||
}
|
||||
|
||||
public static <T> ConfigurationMetadata<T> of(T defaults) {
|
||||
return of(() -> defaults);
|
||||
}
|
||||
|
||||
public static <T> ConfigurationMetadata<T> of(@NotNull Supplier<@Nullable T> defaults) {
|
||||
return new ConfigurationMetadata<>(defaults);
|
||||
}
|
||||
|
||||
protected Supplier<@Nullable T> defaultsSupplier;
|
||||
|
||||
public ConfigurationMetadata(@NotNull Supplier<@Nullable T> defaults) {
|
||||
this.defaultsSupplier = defaults;
|
||||
}
|
||||
|
||||
public boolean isDefault(@NotNull T value) {
|
||||
return value.equals(defaults());
|
||||
}
|
||||
|
||||
public boolean hasDefaults() {
|
||||
return defaults() != null;
|
||||
}
|
||||
|
||||
public T defaultOrSupply(@Nullable T suppliedValue) {
|
||||
return defaultOrSupply(() -> suppliedValue);
|
||||
}
|
||||
|
||||
public T defaultOrSupply(Supplier<@Nullable T> suppliedValue) {
|
||||
T defaults = defaults();
|
||||
return defaults == null ? suppliedValue.get() : defaults;
|
||||
}
|
||||
|
||||
public @Nullable T defaults() {
|
||||
return defaultsSupplier.get();
|
||||
}
|
||||
|
||||
public void setDefaults(Supplier<T> defaultFunction) {
|
||||
this.defaultsSupplier = defaultFunction;
|
||||
}
|
||||
|
||||
public void setDefaults(T value) {
|
||||
setDefaults(() -> value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package cc.carm.lib.configuration.source.meta;
|
||||
|
||||
public interface StandardMeta {
|
||||
|
||||
/**
|
||||
* To mark the {@link cc.carm.lib.configuration.value.ConfigValue} as a minimal unit path.
|
||||
*/
|
||||
ConfigurationMetadata<Boolean> UNIT = ConfigurationMetadata.of(false);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package cc.carm.lib.configuration.source.option;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfigurationOption<V> {
|
||||
|
||||
public static <T> ConfigurationOption<T> of(@NotNull T defaultValue) {
|
||||
return new ConfigurationOption<>(defaultValue);
|
||||
}
|
||||
|
||||
public static <T> ConfigurationOption<T> of(@NotNull Supplier<T> defaultValue) {
|
||||
return of(defaultValue.get());
|
||||
}
|
||||
|
||||
private @NotNull V defaultValue;
|
||||
|
||||
public ConfigurationOption(@NotNull V defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public @NotNull V defaults() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default value of option.
|
||||
*
|
||||
* @param defaultValue Default value
|
||||
*/
|
||||
public void defaults(@NotNull V defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public boolean isDefault(@NotNull V value) {
|
||||
return value.equals(defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package cc.carm.lib.configuration.source.option;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ConfigurationOptionHolder {
|
||||
|
||||
public static @NotNull ConfigurationOptionHolder of(@NotNull Map<ConfigurationOption<?>, Object> options) {
|
||||
return new ConfigurationOptionHolder(new ConcurrentHashMap<>(options));
|
||||
}
|
||||
|
||||
protected final Map<ConfigurationOption<?>, Object> options;
|
||||
|
||||
public ConfigurationOptionHolder() {
|
||||
this(new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
public ConfigurationOptionHolder(Map<ConfigurationOption<?>, Object> options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public @NotNull Map<ConfigurationOption<?>, Object> values() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationOption}
|
||||
* @param <V> Value type
|
||||
* @return Value of option
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> @NotNull V get(@NotNull ConfigurationOption<V> type) {
|
||||
return Optional.ofNullable(values().get(type)).map(v -> (V) v).orElseGet(type::defaults);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of option.
|
||||
*
|
||||
* @param type {@link ConfigurationOption}
|
||||
* @param value Value of option
|
||||
* @param <V> Value type
|
||||
* @return Previous value of option
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V> @Nullable V set(@NotNull ConfigurationOption<V> type, @Nullable V value) {
|
||||
if (value == null) {
|
||||
return (V) values().remove(type);
|
||||
} else {
|
||||
return (V) values().put(type, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of option to option's {@link ConfigurationOption#defaults()}.
|
||||
*
|
||||
* @param type {@link ConfigurationOption}
|
||||
* @param <V> Value type
|
||||
* @return Previous value of option
|
||||
*/
|
||||
public <V> @Nullable V clear(@NotNull ConfigurationOption<V> type) {
|
||||
return set(type, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package cc.carm.lib.configuration.source.option;
|
||||
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
|
||||
import static cc.carm.lib.configuration.source.option.ConfigurationOption.of;
|
||||
|
||||
public interface StandardOptions {
|
||||
|
||||
/**
|
||||
* The configuration path separator.
|
||||
*/
|
||||
ConfigurationOption<Character> PATH_SEPARATOR = of('.');
|
||||
|
||||
/**
|
||||
* Whether to set and save default values if offered and not exists in configuration.
|
||||
*/
|
||||
ConfigurationOption<Boolean> SET_DEFAULTS = of(true);
|
||||
|
||||
/**
|
||||
* Whether to load subclasses of configuration class.
|
||||
*/
|
||||
ConfigurationOption<Boolean> LOAD_SUB_CLASSES = of(true);
|
||||
|
||||
/**
|
||||
* Whether to pre parse the config values.
|
||||
* <br> if false, the values will be parsed when calling
|
||||
* {@link cc.carm.lib.configuration.value.ConfigValue#get()}
|
||||
* <br> if true, the values will be parsed when
|
||||
* {@link ConfigurationHolder#initialize(Configuration)}.
|
||||
*/
|
||||
ConfigurationOption<Boolean> PRELOAD = of(false);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,655 @@
|
||||
package cc.carm.lib.configuration.source.section;
|
||||
|
||||
import cc.carm.lib.configuration.function.DataFunction;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnmodifiableView;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Represents a section of a configuration.
|
||||
*
|
||||
* @author Carm
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public interface ConfigureSection {
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* 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
|
||||
@UnmodifiableView
|
||||
default Set<String> getKeys(boolean deep) {
|
||||
return getValues(deep).keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@UnmodifiableView
|
||||
Map<String, Object> getValues(boolean deep);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*/
|
||||
boolean contains(@NotNull String path);
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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) {
|
||||
return isType(path, List.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@Nullable List<?> getList(@NotNull String path);
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
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
|
||||
ConfigureSection getSection(@NotNull String path);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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")
|
||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaults, @NotNull Class<T> 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")
|
||||
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
|
||||
@NotNull DataFunction<Object, T> parser) {
|
||||
Object value = get(path);
|
||||
if (value != null) {
|
||||
try {
|
||||
return parser.handle(value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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) {
|
||||
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")
|
||||
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
|
||||
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) {
|
||||
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) {
|
||||
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")
|
||||
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
|
||||
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) {
|
||||
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) {
|
||||
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")
|
||||
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
|
||||
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 a int, false otherwise.
|
||||
*/
|
||||
default boolean isInt(@NotNull String path) {
|
||||
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 a int, otherwise 0.
|
||||
*/
|
||||
default @Nullable Integer getInt(@NotNull String path) {
|
||||
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 a int, otherwise the default value.
|
||||
*/
|
||||
@Contract("_, !null -> !null")
|
||||
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
|
||||
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) {
|
||||
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) {
|
||||
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")
|
||||
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
|
||||
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) {
|
||||
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) {
|
||||
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")
|
||||
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
|
||||
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) {
|
||||
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) {
|
||||
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")
|
||||
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
|
||||
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) {
|
||||
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) {
|
||||
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")
|
||||
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
|
||||
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) {
|
||||
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) {
|
||||
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")
|
||||
default @Nullable String getString(@NotNull String path, @Nullable String def) {
|
||||
return get(path, def, String.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of values from the section
|
||||
* <p>
|
||||
* If the path does not exist, an empty list will be returned
|
||||
* <br>Any changes please use {@link #set(String, Object)} after changes
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @param parser The function to parse the values
|
||||
* @param <V> The type of the values
|
||||
* @return The list of values
|
||||
*/
|
||||
default <V> @NotNull List<V> getList(@NotNull String path, @NotNull DataFunction<Object, V> parser) {
|
||||
return getCollection(path, ArrayList::new, parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of strings from the section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of strings
|
||||
*/
|
||||
default @NotNull List<String> getStringList(@NotNull String path) {
|
||||
return getList(path, DataFunction.castToString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of integer from the section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of int values
|
||||
*/
|
||||
default @NotNull List<Integer> getIntegerList(@NotNull String path) {
|
||||
return getList(path, DataFunction.intValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of long from the section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of long values
|
||||
*/
|
||||
default @NotNull List<Long> getLongList(@NotNull String path) {
|
||||
return getList(path, DataFunction.longValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of double from the section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of doubles
|
||||
*/
|
||||
default @NotNull List<Double> getDoubleList(@NotNull String path) {
|
||||
return getList(path, DataFunction.doubleValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of floats from the section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of floats
|
||||
*/
|
||||
default @NotNull List<Float> getFloatList(@NotNull String path) {
|
||||
return getList(path, DataFunction.floatValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of bytes from the section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of bytes
|
||||
*/
|
||||
default @NotNull List<Byte> getByteList(@NotNull String path) {
|
||||
return getList(path, DataFunction.byteValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of char from the section
|
||||
* <p> Limitations see {@link #getList(String, DataFunction)}
|
||||
*
|
||||
* @param path The path to get the list from
|
||||
* @return The list of char
|
||||
*/
|
||||
default @NotNull List<Character> getCharList(@NotNull String path) {
|
||||
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,
|
||||
@NotNull Supplier<C> constructor,
|
||||
@NotNull DataFunction<Object, T> 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) {
|
||||
List<?> values = getList(path);
|
||||
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) {
|
||||
return stream(path).map(parser);
|
||||
}
|
||||
|
||||
static <T, C extends Collection<T>> @NotNull C parseCollection(
|
||||
@Nullable List<?> data, @NotNull Supplier<C> constructor,
|
||||
@NotNull DataFunction<Object, T> parser
|
||||
) {
|
||||
C values = constructor.get();
|
||||
if (data == null) return values;
|
||||
for (Object obj : data) {
|
||||
try {
|
||||
values.add(parser.handle(obj));
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package cc.carm.lib.configuration.source.section;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.option.StandardOptions;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public abstract class ConfigureSource<
|
||||
SECTION extends ConfigureSection, ORIGINAL,
|
||||
SELF extends ConfigureSource<SECTION, ORIGINAL, SELF>>
|
||||
implements ConfigureSection {
|
||||
|
||||
protected final @NotNull ConfigurationHolder<? extends SELF> holder;
|
||||
protected long lastUpdateMillis;
|
||||
|
||||
protected ConfigureSource(@NotNull ConfigurationHolder<? extends SELF> holder, long lastUpdateMillis) {
|
||||
this.holder = holder;
|
||||
this.lastUpdateMillis = lastUpdateMillis;
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationHolder<? extends SELF> holder() {
|
||||
return holder;
|
||||
}
|
||||
|
||||
public void reload() throws Exception {
|
||||
onReload(); // 调用重写的Reload方法
|
||||
this.lastUpdateMillis = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
@ApiStatus.Internal
|
||||
protected abstract @NotNull SELF self();
|
||||
|
||||
/**
|
||||
* @return The original configuration object.
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
public abstract @NotNull ORIGINAL original();
|
||||
|
||||
/**
|
||||
* @return The root {@link ConfigureSection}, which represents the entire configuration.
|
||||
*/
|
||||
public abstract @NotNull SECTION section();
|
||||
|
||||
/**
|
||||
* Save the whole configuration.
|
||||
*
|
||||
* @throws Exception If any error occurs while saving.
|
||||
*/
|
||||
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;
|
||||
|
||||
public char pathSeparator() {
|
||||
return holder().options().get(StandardOptions.PATH_SEPARATOR);
|
||||
}
|
||||
|
||||
public long getLastUpdateMillis() {
|
||||
return this.lastUpdateMillis;
|
||||
}
|
||||
|
||||
public boolean isExpired(long parsedTime) {
|
||||
return getLastUpdateMillis() > parsedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Source also represents the root section, so it has no parent
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
@Override
|
||||
@Contract(pure = true, value = "->null")
|
||||
public @Nullable ConfigureSection parent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
return section().getValues(deep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
section().set(path, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path) {
|
||||
return section().contains(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull String path) {
|
||||
section().remove(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<?> getList(@NotNull String path) {
|
||||
return section().getList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigureSection getSection(@NotNull String path) {
|
||||
return section().getSection(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
return section().get(path);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package cc.carm.lib.configuration.value;
|
||||
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Represents a configurable value with type safety and null-handling capabilities.
|
||||
* <p>
|
||||
* This abstract class provides core functionalities for managing configuration values,
|
||||
* including value retrieval with fallback defaults, null safety enforcement, and value
|
||||
* persistence controls. It serves as the foundation for type-specific configuration
|
||||
* implementations.
|
||||
* </p>
|
||||
*
|
||||
* <h3>Functions:</h3>
|
||||
* <ul>
|
||||
* <li>Type-safe value access through {@link #get()} and {@link #optional()}</li>
|
||||
* <li>Default value fallback via {@link #getOrDefault()}</li>
|
||||
* <li>Null-safety enforcement with {@link #resolve()} and {@link #getNotNull()}</li>
|
||||
* <li>Default value initialization through {@link #setDefault()}</li>
|
||||
* <li>Value comparison with {@link #isDefault()}</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h3>Persistence Behavior:</h3>
|
||||
* Value modifications via {@link #set(Object)} or {@link #setDefault()} methods
|
||||
* <b>do NOT automatically persist</b> to configuration sources. Explicit calls to
|
||||
* {@link ConfigurationHolder#save()} are required for permanent storage.
|
||||
*
|
||||
* @see ValueManifest Base class providing metadata and default value handling
|
||||
* @see ConfigurationHolder Responsible for configuration source persistence
|
||||
*/
|
||||
public abstract class ConfigValue<T> extends ValueManifest<T> {
|
||||
|
||||
protected ConfigValue(@NotNull ValueManifest<T> manifest) {
|
||||
super(manifest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configured value (i.e., the value read from the source).
|
||||
* <br> If no default value was written during initialization, you can use
|
||||
* the {@link #getOrDefault()} method to obtain the default value when this value is empty.
|
||||
*
|
||||
* @return Configured value
|
||||
*/
|
||||
public abstract @Nullable T get();
|
||||
|
||||
/**
|
||||
* Gets the configured value, or returns the default value if not present.
|
||||
*
|
||||
* @return Configured value or default value
|
||||
*/
|
||||
public T getOrDefault() {
|
||||
return optional().orElse(defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the non-null value of this configuration.
|
||||
*
|
||||
* @return Non-null value
|
||||
* @throws NullPointerException Thrown when the corresponding data is null
|
||||
*/
|
||||
public @NotNull T resolve() {
|
||||
return Objects.requireNonNull(getOrDefault(), "Value(" + type() + ") @[" + path() + "] is null.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the non-null value of this configuration.
|
||||
*
|
||||
* @return Non-null value
|
||||
* @throws NullPointerException Thrown when the corresponding data is null
|
||||
* @see #resolve() for a more descriptive function
|
||||
*/
|
||||
public @NotNull T getNotNull() {
|
||||
return resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of this configuration as an {@link Optional}.
|
||||
*
|
||||
* @return {@link Optional} value
|
||||
*/
|
||||
public @NotNull Optional<@Nullable T> optional() {
|
||||
return Optional.ofNullable(get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this configuration.
|
||||
* <br> After setting, the configuration file will NOT be saved automatically.
|
||||
* To save, call {@link ConfigurationHolder#save()}.
|
||||
*
|
||||
* @param value The value to set
|
||||
*/
|
||||
public abstract void set(@Nullable T value);
|
||||
|
||||
/**
|
||||
* Initializes the default value for this configuration.
|
||||
* <br> After setting, the configuration file will NOT be saved automatically.
|
||||
* To save, call {@link ConfigurationHolder#save()}.
|
||||
*/
|
||||
public void setDefault() {
|
||||
setDefault(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configuration value to its default.
|
||||
* <br> After setting, the configuration file will NOT be saved automatically.
|
||||
* To save, call {@link ConfigurationHolder#save()}.
|
||||
*
|
||||
* @param override Whether to overwrite existing configured value
|
||||
*/
|
||||
public void setDefault(boolean override) {
|
||||
if (config().contains(path()) && !override) return; // Skip if the value is already set
|
||||
set(defaults()); // Set the default value
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the loaded configuration value matches the default value.
|
||||
*
|
||||
* @return Whether the current value is the default value
|
||||
*/
|
||||
public boolean isDefault() {
|
||||
return Objects.equals(defaults(), get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to save the configuration.
|
||||
* <br>To save multiple modifications,
|
||||
* it is recommended to call {@link ConfigurationHolder#save()}
|
||||
* after all modifications are completed instead of this.
|
||||
*
|
||||
* @throws Exception Thrown when an error occurs during saving
|
||||
*/
|
||||
public void save() throws Exception {
|
||||
holder().save();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package cc.carm.lib.configuration.value;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSource;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ValueManifest<T> {
|
||||
|
||||
protected final @NotNull ValueType<T> type;
|
||||
protected final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer;
|
||||
|
||||
protected @Nullable ConfigurationHolder<?> holder;
|
||||
protected @Nullable String path; // Section path
|
||||
|
||||
protected @NotNull Supplier<@Nullable T> defaultSupplier;
|
||||
|
||||
|
||||
public ValueManifest(@NotNull ValueType<T> type) {
|
||||
this(type, () -> null, EMPTY_INITIALIZER, null, null);
|
||||
}
|
||||
|
||||
public ValueManifest(@NotNull T defaultValue) {
|
||||
this(ValueType.of(defaultValue), () -> defaultValue);
|
||||
}
|
||||
|
||||
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier) {
|
||||
this(type, defaultSupplier, EMPTY_INITIALIZER, null, null);
|
||||
}
|
||||
|
||||
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier,
|
||||
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer) {
|
||||
this(type, defaultSupplier, initializer, null, null);
|
||||
}
|
||||
|
||||
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier,
|
||||
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer,
|
||||
@Nullable ConfigurationHolder<?> holder, @Nullable String path) {
|
||||
this.type = type;
|
||||
this.initializer = initializer;
|
||||
this.defaultSupplier = defaultSupplier;
|
||||
this.holder = holder;
|
||||
this.path = path;
|
||||
initialize();
|
||||
}
|
||||
|
||||
protected ValueManifest(@NotNull ValueManifest<T> manifest) {
|
||||
this(manifest.type, manifest.defaultSupplier, manifest.initializer, manifest.holder, manifest.path);
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ConfigurationHolder<?> holder, @NotNull String path) {
|
||||
this.holder = holder;
|
||||
this.path = path;
|
||||
initialize();
|
||||
}
|
||||
|
||||
protected void initialize() {
|
||||
if (holder != null && path != null) this.initializer.accept(holder, path);
|
||||
}
|
||||
|
||||
public @NotNull ValueType<T> type() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void holder(@NotNull ConfigurationHolder<?> holder) {
|
||||
this.holder = holder;
|
||||
}
|
||||
|
||||
public void path(@NotNull String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public @Nullable T defaults() {
|
||||
return this.defaultSupplier.get();
|
||||
}
|
||||
|
||||
public void defaults(@Nullable T defaultValue) {
|
||||
defaults(() -> defaultValue);
|
||||
}
|
||||
|
||||
public void defaults(@NotNull Supplier<@Nullable T> defaultValue) {
|
||||
this.defaultSupplier = defaultValue;
|
||||
}
|
||||
|
||||
public boolean hasDefaults() {
|
||||
return this.defaultSupplier.get() != null;
|
||||
}
|
||||
|
||||
public @NotNull String path() {
|
||||
if (path != null) return path;
|
||||
else throw new IllegalStateException("No section path provided.");
|
||||
}
|
||||
|
||||
public @NotNull ConfigurationHolder<?> holder() {
|
||||
if (this.holder != null) return this.holder;
|
||||
throw new IllegalStateException("Value does not have a provider.");
|
||||
}
|
||||
|
||||
public @NotNull ConfigureSource<?, ?, ?> config() {
|
||||
return holder().config();
|
||||
}
|
||||
|
||||
public ConfigurationMetaHolder metadata() {
|
||||
return holder().metadata(path());
|
||||
}
|
||||
|
||||
protected Object getData() {
|
||||
return config().get(path());
|
||||
}
|
||||
|
||||
protected void setData(@Nullable Object value) {
|
||||
config().set(path(), value);
|
||||
}
|
||||
|
||||
|
||||
private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, path) -> {
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package cc.carm.lib.configuration.value.impl;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class CachedConfigValue<T> extends ConfigValue<T> {
|
||||
|
||||
protected @Nullable T cachedValue;
|
||||
protected long parsedTime = -1;
|
||||
|
||||
protected CachedConfigValue(@NotNull ValueManifest<T> manifest) {
|
||||
super(manifest);
|
||||
}
|
||||
|
||||
protected T updateCache(T value) {
|
||||
this.parsedTime = System.currentTimeMillis();
|
||||
this.cachedValue = value;
|
||||
return getCachedValue();
|
||||
}
|
||||
|
||||
public @Nullable T getCachedValue() {
|
||||
return cachedValue;
|
||||
}
|
||||
|
||||
public boolean cacheExpired() {
|
||||
return this.parsedTime <= 0 || config().isExpired(this.parsedTime);
|
||||
}
|
||||
|
||||
protected final T getDefaultFirst(@Nullable T value) {
|
||||
return updateCache(this.defaults() == null ? value : this.defaults());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached value or the default value if the cached value is null
|
||||
*
|
||||
* @return the cached value or the default value
|
||||
*/
|
||||
protected @Nullable T getCachedOrDefault() {
|
||||
return getCachedOrDefault(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached value or the default value if the cached value is null
|
||||
*
|
||||
* @param emptyValue the value to return if the cached value and the default value are null
|
||||
* @return the cached value or the default value
|
||||
*/
|
||||
@Contract("!null->!null")
|
||||
protected T getCachedOrDefault(@Nullable T emptyValue) {
|
||||
if (getCachedValue() != null) return getCachedValue();
|
||||
else if (defaults() != null) return defaults();
|
||||
else return emptyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param adapter Value adapter
|
||||
* @param <O> Base object type
|
||||
* @return Value's parser, parse base object to value.
|
||||
*/
|
||||
protected <O> @Nullable ValueParser<O> parserFor(@NotNull ValueAdapter<O> adapter) {
|
||||
if (adapter.parser() != null) return adapter.parser();
|
||||
ValueAdapter<O> registered = holder().adapters().adapterOf(adapter.type());
|
||||
if (registered == null) return null;
|
||||
return registered.parser();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param adapter Value adapter
|
||||
* @param <O> Base object type
|
||||
* @return Value's serializer, parse value to base object.
|
||||
*/
|
||||
protected <O> @Nullable ValueSerializer<O> serializerFor(@NotNull ValueAdapter<O> adapter) {
|
||||
if (adapter.serializer() != null) return adapter.serializer();
|
||||
ValueAdapter<O> registered = holder().adapters().adapterOf(adapter.type());
|
||||
if (registered == null) return null;
|
||||
return registered.serializer();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package cc.carm.lib.configuration.value.standard;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.list.ConfigListBuilder;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements List<V> {
|
||||
|
||||
public static <T> @NotNull ConfigListBuilder<T> builderOf(@NotNull Class<T> type) {
|
||||
return builderOf(ValueType.of(type));
|
||||
}
|
||||
|
||||
public static <T> @NotNull ConfigListBuilder<T> builderOf(@NotNull ValueType<T> type) {
|
||||
return new ConfigListBuilder<>(type);
|
||||
}
|
||||
|
||||
protected final @NotNull Supplier<? extends List<V>> constructor;
|
||||
protected final @NotNull ValueAdapter<V> paramAdapter;
|
||||
|
||||
public ConfiguredList(@NotNull ValueManifest<List<V>> manifest,
|
||||
@NotNull Supplier<? extends List<V>> constructor,
|
||||
@NotNull ValueAdapter<V> paramAdapter) {
|
||||
super(manifest);
|
||||
this.constructor = constructor;
|
||||
this.paramAdapter = paramAdapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Adapter of this value.
|
||||
*/
|
||||
public @NotNull ValueAdapter<V> adapter() {
|
||||
return this.paramAdapter;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<V> paramType() {
|
||||
return adapter().type();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value's parser, parse base object to value.
|
||||
*/
|
||||
public @Nullable ValueParser<V> parser() {
|
||||
return parserFor(adapter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value's serializer, parse value to base object.
|
||||
*/
|
||||
public @Nullable ValueSerializer<V> serializer() {
|
||||
return serializerFor(adapter());
|
||||
}
|
||||
|
||||
private @NotNull List<V> createList() {
|
||||
return constructor.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<V> get() {
|
||||
if (!cacheExpired()) return getCachedOrDefault(createList());
|
||||
// Data that is outdated and needs to be parsed again.
|
||||
List<V> list = createList();
|
||||
List<?> data = config().contains(path()) ? config().getList(path()) : null;
|
||||
if (data == null) return getDefaultFirst(list);
|
||||
|
||||
ValueParser<V> parser = parser();
|
||||
if (parser == null) return getDefaultFirst(list);
|
||||
|
||||
for (Object dataVal : data) {
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
list.add(parser.parse(holder(), paramType(), dataVal));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return updateCache(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@Nullable List<V> value) {
|
||||
updateCache(value);
|
||||
if (value == null) {
|
||||
setData(null);
|
||||
return;
|
||||
}
|
||||
|
||||
ValueSerializer<V> serializer = serializer();
|
||||
if (serializer == null) return;
|
||||
List<Object> data = new ArrayList<>();
|
||||
for (V val : value) {
|
||||
if (val == null) continue;
|
||||
try {
|
||||
data.add(serializer.serialize(holder(), paramType(), val));
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
setData(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(int index) {
|
||||
return resolve().get(index);
|
||||
}
|
||||
|
||||
public @NotNull List<V> copy() {
|
||||
return new ArrayList<>(resolve());
|
||||
}
|
||||
|
||||
public <T> @NotNull T handle(Function<List<V>, T> function) {
|
||||
List<V> list = resolve();
|
||||
T result = function.apply(list);
|
||||
set(list);
|
||||
return result;
|
||||
}
|
||||
|
||||
public @NotNull ConfiguredList<V> modify(Consumer<List<V>> consumer) {
|
||||
List<V> list = resolve();
|
||||
consumer.accept(list);
|
||||
set(list);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V set(int index, V element) {
|
||||
return handle(list -> list.set(index, element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return resolve().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return resolve().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return resolve().contains(o);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Iterator<V> iterator() {
|
||||
return resolve().iterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Object @NotNull [] toArray() {
|
||||
return resolve().toArray();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> T @NotNull [] toArray(@NotNull T[] a) {
|
||||
return resolve().toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsAll(@NotNull Collection<?> c) {
|
||||
return new HashSet<>(resolve()).containsAll(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(V v) {
|
||||
handle(list -> list.add(v));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(int index, V element) {
|
||||
modify(list -> list.add(index, element));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(@NotNull Collection<? extends V> c) {
|
||||
return handle(list -> list.addAll(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(int index, @NotNull Collection<? extends V> c) {
|
||||
return handle(list -> list.addAll(index, c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return handle(list -> list.remove(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(int index) {
|
||||
return handle(list -> list.remove(index));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll(@NotNull Collection<?> c) {
|
||||
return handle(list -> list.removeAll(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retainAll(@NotNull Collection<?> c) {
|
||||
return handle(list -> list.retainAll(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
modify(List::clear);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexOf(Object o) {
|
||||
return resolve().indexOf(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int lastIndexOf(Object o) {
|
||||
return resolve().lastIndexOf(o);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<V> listIterator() {
|
||||
return resolve().listIterator();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ListIterator<V> listIterator(int index) {
|
||||
return resolve().listIterator(index);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<V> subList(int fromIndex, int toIndex) {
|
||||
return resolve().subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package cc.carm.lib.configuration.value.standard;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.map.ConfigMapCreator;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements Map<K, V> {
|
||||
|
||||
public static <K, V> ConfigMapCreator<K, V> builderOf(@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
|
||||
return new ConfigMapCreator<>(keyType, valueType);
|
||||
}
|
||||
|
||||
public static <K, V> ConfigMapCreator<K, V> builderOf(@NotNull Class<K> keyType, @NotNull Class<V> valueType) {
|
||||
return new ConfigMapCreator<>(ValueType.of(keyType), ValueType.of(valueType));
|
||||
}
|
||||
|
||||
public static <K, V> ConfiguredMap<K, V> of(@NotNull Supplier<? extends Map<K, V>> constructor,
|
||||
@NotNull ValueAdapter<K> keyAdapter, @NotNull ValueAdapter<V> valueAdapter) {
|
||||
return new ConfiguredMap<>(new ValueManifest<>(new ValueType<Map<K, V>>() {
|
||||
}, constructor::get), constructor, keyAdapter, valueAdapter);
|
||||
}
|
||||
|
||||
protected final @NotNull Supplier<? extends Map<K, V>> constructor;
|
||||
|
||||
protected final @NotNull ValueAdapter<K> keyAdapter;
|
||||
protected final @NotNull ValueAdapter<V> valueAdapter;
|
||||
|
||||
public ConfiguredMap(@NotNull ValueManifest<Map<K, V>> manifest,
|
||||
@NotNull Supplier<? extends Map<K, V>> constructor,
|
||||
@NotNull ValueAdapter<K> keyAdapter, @NotNull ValueAdapter<V> valueAdapter) {
|
||||
super(manifest);
|
||||
this.constructor = constructor;
|
||||
this.keyAdapter = keyAdapter;
|
||||
this.valueAdapter = valueAdapter;
|
||||
}
|
||||
|
||||
public @NotNull ValueAdapter<K> keyAdapter() {
|
||||
return keyAdapter;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<K> keyType() {
|
||||
return keyAdapter().type();
|
||||
}
|
||||
|
||||
public @NotNull ValueAdapter<V> valueAdapter() {
|
||||
return valueAdapter;
|
||||
}
|
||||
|
||||
public @NotNull ValueType<V> valueType() {
|
||||
return valueAdapter().type();
|
||||
}
|
||||
|
||||
private Map<K, V> createMap() {
|
||||
return this.constructor.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<K, V> get() {
|
||||
if (!cacheExpired()) return getCachedOrDefault(createMap());
|
||||
// If the value is expired, we need to update it
|
||||
Map<K, V> map = createMap();
|
||||
|
||||
ConfigureSection section = config().getSection(path());
|
||||
if (section == null) return getDefaultFirst(map);
|
||||
|
||||
Set<String> keys = section.getKeys(false);
|
||||
if (keys.isEmpty()) return getDefaultFirst(map);
|
||||
|
||||
ValueParser<K> keyParser = parserFor(keyAdapter);
|
||||
ValueParser<V> valueParser = parserFor(valueAdapter);
|
||||
if (keyParser == null || valueParser == null) return getDefaultFirst(map);
|
||||
|
||||
for (String dataKey : keys) {
|
||||
Object dataVal = section.get(dataKey);
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
K key = keyParser.parse(holder(), keyType(), dataKey);
|
||||
V value = valueParser.parse(holder(), valueType(), dataVal);
|
||||
map.put(key, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return updateCache(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(Object key) {
|
||||
return get().get(key);
|
||||
}
|
||||
|
||||
public V getNotNull(@Nullable K key) {
|
||||
return Objects.requireNonNull(get(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@Nullable Map<K, V> value) {
|
||||
updateCache(value);
|
||||
if (value == null) {
|
||||
setData(null);
|
||||
return;
|
||||
}
|
||||
|
||||
ValueSerializer<K> keySerializer = serializerFor(keyAdapter);
|
||||
ValueSerializer<V> valueSerializer = serializerFor(valueAdapter);
|
||||
if (keySerializer == null || valueSerializer == null) return;
|
||||
|
||||
Map<Object, Object> data = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<K, V> entry : value.entrySet()) {
|
||||
try {
|
||||
data.put(
|
||||
keySerializer.serialize(holder(), keyType(), entry.getKey()),
|
||||
valueSerializer.serialize(holder(), valueType(), entry.getValue())
|
||||
);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public <T> @NotNull T handle(Function<Map<K, V>, T> function) {
|
||||
Map<K, V> m = get();
|
||||
T result = function.apply(m);
|
||||
set(m);
|
||||
return result;
|
||||
}
|
||||
|
||||
public @NotNull ConfiguredMap<K, V> modify(Consumer<Map<K, V>> consumer) {
|
||||
Map<K, V> m = get();
|
||||
consumer.accept(m);
|
||||
set(m);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return get().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return get().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return get().containsKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return get().containsValue(value);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
return handle(m -> m.put(key, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(Object key) {
|
||||
return handle(m -> m.remove(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
|
||||
modify(map -> map.putAll(m));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
modify(Map::clear);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return get().keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<V> values() {
|
||||
return get().values();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@Unmodifiable
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return get().entrySet();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package cc.carm.lib.configuration.value.standard;
|
||||
|
||||
import cc.carm.lib.configuration.adapter.ValueAdapter;
|
||||
import cc.carm.lib.configuration.adapter.ValueParser;
|
||||
import cc.carm.lib.configuration.adapter.ValueSerializer;
|
||||
import cc.carm.lib.configuration.adapter.ValueType;
|
||||
import cc.carm.lib.configuration.builder.value.ConfigValueBuilder;
|
||||
import cc.carm.lib.configuration.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfiguredValue<V> extends CachedConfigValue<V> {
|
||||
|
||||
public static <V> ConfigValueBuilder<V> builderOf(@NotNull Class<V> type) {
|
||||
return new ConfigValueBuilder<>(ValueType.of(type));
|
||||
}
|
||||
|
||||
public static <V> ConfigValueBuilder<V> builderOf(@NotNull ValueType<V> type) {
|
||||
return new ConfigValueBuilder<>(type);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull V defaults) {
|
||||
return of(ValueType.of(defaults), () -> defaults);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull Class<V> type) {
|
||||
return of(ValueType.of(type), () -> null);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull Class<V> type, @NotNull V defaults) {
|
||||
return of(ValueType.of(type), () -> defaults);
|
||||
}
|
||||
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull Class<V> type, @NotNull Supplier<@Nullable V> defaultSupplier) {
|
||||
return of(ValueType.of(type), defaultSupplier);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull ValueType<V> type) {
|
||||
return of(type, () -> null);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull ValueType<V> type, @NotNull Supplier<@Nullable V> defaultSupplier) {
|
||||
return of(
|
||||
new ValueManifest<>(type, defaultSupplier),
|
||||
(provider, t, data) -> provider.deserialize(type, data),
|
||||
(provider, t, value) -> provider.serialize(value)
|
||||
);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V> manifest,
|
||||
@Nullable ValueParser<V> parser,
|
||||
@Nullable ValueSerializer<V> serializer) {
|
||||
ValueAdapter<V> adapter = new ValueAdapter<>(manifest.type());
|
||||
adapter.parser(parser);
|
||||
adapter.serializer(serializer);
|
||||
return of(manifest, adapter);
|
||||
}
|
||||
|
||||
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V> manifest, @NotNull ValueAdapter<V> adapter) {
|
||||
return new ConfiguredValue<>(manifest, adapter);
|
||||
}
|
||||
|
||||
protected final @NotNull ValueAdapter<V> adapter;
|
||||
|
||||
public ConfiguredValue(@NotNull ValueManifest<V> manifest, @NotNull ValueAdapter<V> adapter) {
|
||||
super(manifest);
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Adapter of this value.
|
||||
*/
|
||||
public @NotNull ValueAdapter<V> adapter() {
|
||||
return adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value's parser, parse base object to value.
|
||||
*/
|
||||
public @Nullable ValueParser<V> parser() {
|
||||
return parserFor(adapter());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value's serializer, parse value to base object.
|
||||
*/
|
||||
public @Nullable ValueSerializer<V> serializer() {
|
||||
return serializerFor(adapter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get() {
|
||||
if (!cacheExpired()) return getCachedOrDefault();
|
||||
// Data that is outdated and needs to be parsed again.
|
||||
|
||||
Object data = getData();
|
||||
if (data == null) return defaults();
|
||||
|
||||
ValueParser<V> parser = parser();
|
||||
if (parser == null) return defaults(); // No parser, return default value.
|
||||
|
||||
try {
|
||||
// If there are no errors, update the cache and return.
|
||||
return updateCache(parser.parse(holder(), type(), data));
|
||||
} catch (Exception e) {
|
||||
// There was a parsing error, prompted and returned the default value.
|
||||
e.printStackTrace();
|
||||
return defaults();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value of the configuration path.
|
||||
* Will use {@link #serializer()} to serialize the value.
|
||||
*
|
||||
* @param value The value to be set
|
||||
*/
|
||||
@Override
|
||||
public void set(V value) {
|
||||
updateCache(value); // Update cache
|
||||
if (value == null) {
|
||||
setData(null);
|
||||
return;
|
||||
}
|
||||
|
||||
ValueSerializer<V> serializer = serializer();
|
||||
if (serializer == null) return; // No serializer, do nothing.
|
||||
|
||||
try {
|
||||
setData(serializer.serialize(holder(), type(), value));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.source.loader.PathGenerator;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NameTest {
|
||||
@@ -7,10 +7,10 @@ public class NameTest {
|
||||
@Test
|
||||
public void onTest() {
|
||||
|
||||
System.out.println(ConfigInitializer.getPathFromName("LoveGames")); // -> love-games
|
||||
System.out.println(ConfigInitializer.getPathFromName("EASY_GAME")); // -> easy-game
|
||||
System.out.println(ConfigInitializer.getPathFromName("F")); //-? f
|
||||
System.out.println(ConfigInitializer.getPathFromName("Test123123")); // -? test123123123
|
||||
System.out.println(PathGenerator.covertPathName("LoveGames")); // -> love-games
|
||||
System.out.println(PathGenerator.covertPathName("EASY_GAME")); // -> easy-game
|
||||
System.out.println(PathGenerator.covertPathName("F")); //-? f
|
||||
System.out.println(PathGenerator.covertPathName("Test123123")); // -? test123123123
|
||||
|
||||
}
|
||||
|
||||
|
||||
+8
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>3.6.0</version>
|
||||
<version>4.0.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
@@ -26,6 +26,13 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-feature-commentable</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package cc.carm.lib.configuration.demo;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
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.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
|
||||
@HeaderComment({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
|
||||
public class DatabaseConfiguration extends ConfigurationRoot {
|
||||
@HeaderComments({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
|
||||
public class DatabaseConfiguration implements Configuration {
|
||||
|
||||
@ConfigPath("driver")
|
||||
@HeaderComment({
|
||||
@HeaderComments({
|
||||
"数据库驱动配置,请根据数据库类型设置。",
|
||||
"- MySQL(旧): com.mysql.jdbc.Driver",
|
||||
"- MySQL(新): com.mysql.cj.jdbc.Driver",
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package cc.carm.lib.configuration.demo.tests;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.conf.TestConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
||||
import cc.carm.lib.configuration.demo.tests.conf.RegistryConfig;
|
||||
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
|
||||
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -15,8 +14,12 @@ import java.util.stream.IntStream;
|
||||
public class ConfigurationTest {
|
||||
|
||||
@TestOnly
|
||||
public static void testDemo(ConfigurationProvider<?> provider) {
|
||||
provider.initialize(DemoConfiguration.class);
|
||||
public static void testDemo(ConfigurationHolder<?> holder) {
|
||||
try {
|
||||
holder.initialize(DemoConfiguration.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
System.out.println("----------------------------------------------------");
|
||||
|
||||
@@ -27,57 +30,57 @@ public class ConfigurationTest {
|
||||
System.out.println("after: " + DemoConfiguration.TEST_NUMBER.get());
|
||||
|
||||
System.out.println("> Test Value:");
|
||||
System.out.println("before: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get());
|
||||
DemoConfiguration.Sub.UUID_CONFIG_VALUE.set(UUID.randomUUID());
|
||||
System.out.println("after: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get());
|
||||
System.out.println("before: " + DemoConfiguration.SUB.UUID_CONFIG_VALUE.get());
|
||||
DemoConfiguration.SUB.UUID_CONFIG_VALUE.set(UUID.randomUUID());
|
||||
System.out.println("after: " + DemoConfiguration.SUB.UUID_CONFIG_VALUE.get());
|
||||
|
||||
System.out.println("> Test List:");
|
||||
|
||||
System.out.println(" Before:");
|
||||
DemoConfiguration.Sub.That.OPERATORS.forEach(System.out::println);
|
||||
DemoConfiguration.SUB.That.OPERATORS.forEach(System.out::println);
|
||||
List<UUID> operators = IntStream.range(0, 5).mapToObj(i -> UUID.randomUUID()).collect(Collectors.toList());
|
||||
DemoConfiguration.Sub.That.OPERATORS.set(operators);
|
||||
DemoConfiguration.SUB.That.OPERATORS.set(operators);
|
||||
System.out.println(" After:");
|
||||
DemoConfiguration.Sub.That.OPERATORS.forEach(System.out::println);
|
||||
DemoConfiguration.SUB.That.OPERATORS.forEach(System.out::println);
|
||||
|
||||
System.out.println("> Clear List:");
|
||||
System.out.println(" Before: size :" + DemoConfiguration.Sub.That.OPERATORS.size());
|
||||
DemoConfiguration.Sub.That.OPERATORS.modify(List::clear);
|
||||
System.out.println(" After size :" + DemoConfiguration.Sub.That.OPERATORS.size());
|
||||
System.out.println(" Before: size :" + DemoConfiguration.SUB.That.OPERATORS.size());
|
||||
DemoConfiguration.SUB.That.OPERATORS.modify(List::clear);
|
||||
System.out.println(" After size :" + DemoConfiguration.SUB.That.OPERATORS.size());
|
||||
|
||||
System.out.println("> Test Section:");
|
||||
System.out.println(DemoConfiguration.MODEL_TEST.get());
|
||||
DemoConfiguration.MODEL_TEST.set(TestModel.random());
|
||||
System.out.println(DemoConfiguration.ALLOWLISTS.get());
|
||||
DemoConfiguration.ALLOWLISTS.add(UserRecord.random());
|
||||
|
||||
System.out.println("> Test Maps:");
|
||||
DemoConfiguration.USERS.forEach((k, v) -> System.out.println(k + ": " + v));
|
||||
LinkedHashMap<Integer, UUID> data = new LinkedHashMap<>();
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
data.put(i, UUID.randomUUID());
|
||||
}
|
||||
DemoConfiguration.USERS.set(data);
|
||||
// System.out.println("> Test Maps:");
|
||||
// DemoConfiguration.USERS.forEach((k, v) -> System.out.println(k + ": " + v));
|
||||
// LinkedHashMap<Integer, UUID> data = new LinkedHashMap<>();
|
||||
// for (int i = 1; i <= 5; i++) {
|
||||
// data.put(i, UUID.randomUUID());
|
||||
// }
|
||||
// DemoConfiguration.USERS.set(data);
|
||||
System.out.println("----------------------------------------------------");
|
||||
}
|
||||
|
||||
public static void testInner(ConfigurationProvider<?> provider) {
|
||||
public static void testInner(ConfigurationHolder<?> provider) {
|
||||
|
||||
TestConfiguration TEST = new TestConfiguration();
|
||||
RegistryConfig TEST = new RegistryConfig();
|
||||
|
||||
provider.initialize(TEST, true);
|
||||
provider.initialize(TEST);
|
||||
|
||||
System.out.println("> Test Inner value before:");
|
||||
System.out.println(TEST.INNER.INNER_VALUE.getNotNull());
|
||||
System.out.println(TEST.INSTANCE.INNER_VALUE.resolve());
|
||||
|
||||
double after = Math.random() * 200D;
|
||||
System.out.println("> Test Inner value -> " + after);
|
||||
TEST.INNER.INNER_VALUE.set(after);
|
||||
TEST.INSTANCE.INNER_VALUE.set(after);
|
||||
|
||||
System.out.println("> Test Inner value after:");
|
||||
System.out.println(TEST.INNER.INNER_VALUE.getNotNull());
|
||||
System.out.println(TEST.INSTANCE.INNER_VALUE.resolve());
|
||||
|
||||
}
|
||||
|
||||
public static void save(ConfigurationProvider<?> provider) {
|
||||
public static void save(ConfigurationHolder<?> provider) {
|
||||
try {
|
||||
provider.save();
|
||||
} catch (Exception e) {
|
||||
|
||||
+58
-41
@@ -1,73 +1,90 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.annotation.InlineComment;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
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.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.UserRecord;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredList;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredMap;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
@HeaderComment({"此处内容将显示在配置文件的最上方"})
|
||||
public class DemoConfiguration extends ConfigurationRoot {
|
||||
@ConfigPath(root = true)
|
||||
@HeaderComments({"此处内容将显示在配置文件的最上方"})
|
||||
@FooterComments({
|
||||
"------------------------------------------------",
|
||||
"此处内容将显示在配置文件的最下方",
|
||||
"可用于显示版权信息等",
|
||||
"感谢您使用 https://github.com/CarmJos/EasyConfiguration !"
|
||||
})
|
||||
public interface DemoConfiguration extends Configuration {
|
||||
|
||||
@ConfigPath(root = true)
|
||||
protected static final ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
|
||||
ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
|
||||
|
||||
@ConfigPath(root = true)
|
||||
public static final ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(Long.class, 1000000L);
|
||||
@FooterComments({"此处内容将显示在配置条目的下方", "可用于补充说明,但一般不建议使用"})
|
||||
ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(1000000L);
|
||||
|
||||
@HeaderComments({"枚举类型测试"})
|
||||
@FooterComments({"上述的枚举内容本质上是通过STRING解析的"})
|
||||
ConfigValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS);
|
||||
|
||||
// 支持通过 Class<?> 变量标注子配置,一并注册。
|
||||
// 注意: 若对应类也有注解,则优先使用类的注解。
|
||||
@ConfigPath("other-class-config") //支持通过注解修改子配置的主路径,若不修改则以变量名自动生成。
|
||||
@HeaderComment({"", "Something..."}) // 支持给子路径直接打注释
|
||||
@InlineComment("InlineComments for class path")
|
||||
public static final Class<?> OTHER = OtherConfiguration.class;
|
||||
Class<?> DATABASE = DatabaseConfiguration.class;
|
||||
|
||||
@ConfigPath("user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
||||
@InlineComment("Section数据也支持InlineComment注释")
|
||||
public static final ConfigValue<TestModel> MODEL_TEST = ConfiguredSection
|
||||
.builderOf(TestModel.class)
|
||||
.defaults(new TestModel("Carm", UUID.randomUUID()))
|
||||
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
|
||||
.serializeValue(TestModel::serialize).build();
|
||||
@ConfigPath("registered_users") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||
@HeaderComments({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
||||
@InlineComment("默认地注释会加到Section的首行末尾") // 通过注解给配置添加注释。
|
||||
ConfiguredList<UserRecord> ALLOWLISTS = ConfiguredList.builderOf(UserRecord.class).fromSection()
|
||||
.parse(UserRecord::deserialize).serialize(UserRecord::serialize)
|
||||
.defaults(UserRecord.CARM).build();
|
||||
|
||||
@HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"})
|
||||
public static final ConfiguredMap<Integer, UUID> USERS = ConfiguredMap
|
||||
.builderOf(Integer.class, UUID.class)
|
||||
.asLinkedMap().fromString()
|
||||
.parseKey(Integer::parseInt)
|
||||
.parseValue(v -> Objects.requireNonNull(UUID.fromString(v)))
|
||||
@HeaderComments({
|
||||
"------------------------------------------------",
|
||||
"[ID - ItemStack]对照表", "", "用于测试Map类型的解析与序列化保存"
|
||||
})
|
||||
@FooterComments("------------------------------------------------")
|
||||
ConfiguredMap<Integer, ItemStack> ITEMS = ConfiguredMap.builderOf(Integer.class, ItemStack.class)
|
||||
.asLinkedMap().fromSection()
|
||||
.parseKey(data -> Integer.parseInt(data))
|
||||
.parse(ItemStack::deserialize).serialize(ItemStack::serialize)
|
||||
.defaults(m -> {
|
||||
m.put(1, new ItemStack("stone", 64));
|
||||
m.put(2, new ItemStack("iron", 64, "铁锭", Arrays.asList("一些铁锭", "可以制造东西")));
|
||||
})
|
||||
.build();
|
||||
|
||||
|
||||
/**
|
||||
* 支持内部类的直接注册。
|
||||
* 注意,需要使用 {@link ConfigInitializer#initialize(Class, boolean, boolean)} 方法,并设定第三个参数为 true。
|
||||
* 注意,需要启用 {@link cc.carm.lib.configuration.source.option.StandardOptions#LOAD_SUB_CLASSES}
|
||||
*/
|
||||
public static class Sub extends ConfigurationRoot {
|
||||
class SUB implements Configuration {
|
||||
|
||||
@ConfigPath(value = "uuid-value", root = true)
|
||||
@InlineComment("This is an inline comment")
|
||||
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
|
||||
.builderOf(UUID.class).fromString()
|
||||
.parseValue((data, defaultValue) -> UUID.fromString(data))
|
||||
.parse((holder, data) -> UUID.fromString(data))
|
||||
.build();
|
||||
|
||||
public static class That extends ConfigurationRoot {
|
||||
@HeaderComments({"内部类的内部类测试", "通过这种方式,您可以轻易实现多层次的配置文件结构"})
|
||||
@FooterComments({"-------------"})
|
||||
public interface That extends Configuration {
|
||||
|
||||
public static final ConfiguredList<UUID> OPERATORS = ConfiguredList
|
||||
ConfiguredList<UUID> OPERATORS = ConfiguredList
|
||||
.builderOf(UUID.class).fromString()
|
||||
.parseValue(s -> Objects.requireNonNull(UUID.fromString(s)))
|
||||
.parse(s -> Objects.requireNonNull(UUID.fromString(s)))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
import cc.carm.lib.configuration.annotation.HeaderComments;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
|
||||
@HeaderComments("Inner Test")
|
||||
public class InstanceConfig implements Configuration {
|
||||
|
||||
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(1.0D);
|
||||
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
public class OtherConfiguration {
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.Configuration;
|
||||
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.InlineComment;
|
||||
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
|
||||
import cc.carm.lib.configuration.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class RegistryConfig implements Configuration {
|
||||
|
||||
@HeaderComments("Support for configurations as instances")
|
||||
public final InstanceConfig INSTANCE = new InstanceConfig();
|
||||
|
||||
@ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||
@FooterComments({"12313213212"})
|
||||
@InlineComment(value = "用户名(匹配注释)", regex = "name") // 通过注解给配置添加注释。
|
||||
@InlineComment(value = "信息", regex = {"info.*", "info.game.*"}) // 通过注解给配置添加注释。
|
||||
public final ConfigValue<UserRecord> TEST_MODEL = ConfiguredValue.builderOf(UserRecord.class).fromSection()
|
||||
.defaults(new UserRecord("Carm", UUID.randomUUID()))
|
||||
.parse((holder, section) -> UserRecord.deserialize(section))
|
||||
.serialize((holder, data) -> data.serialize()).build();
|
||||
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.annotation.InlineComment;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class TestConfiguration extends ConfigurationRoot {
|
||||
|
||||
public final TestInnerConfiguration INNER = new TestInnerConfiguration();
|
||||
|
||||
public final ConfigValue<Double> CLASS_VALUE = ConfiguredValue.of(Double.class, 1.0D);
|
||||
|
||||
@ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
||||
@InlineComment("Section数据也支持InlineComment注释")
|
||||
public final ConfigValue<TestModel> TEST_MODEL = ConfiguredSection
|
||||
.builderOf(TestModel.class)
|
||||
.defaults(new TestModel("Carm", UUID.randomUUID()))
|
||||
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
|
||||
.serializeValue(TestModel::serialize).build();
|
||||
|
||||
|
||||
}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
|
||||
@HeaderComment("Inner Test")
|
||||
public class TestInnerConfiguration extends ConfigurationRoot {
|
||||
|
||||
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(Double.class, 1.0D);
|
||||
|
||||
}
|
||||
+2
-2
@@ -2,11 +2,11 @@ package cc.carm.lib.configuration.demo.tests.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class AbstractModel {
|
||||
public abstract class AbstractRecord {
|
||||
|
||||
protected final @NotNull String name;
|
||||
|
||||
public AbstractModel(@NotNull String name) {
|
||||
public AbstractRecord(@NotNull String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
package cc.carm.lib.configuration.demo.tests.model;
|
||||
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ItemStack {
|
||||
|
||||
protected String material;
|
||||
protected int amount;
|
||||
protected @Nullable String name;
|
||||
protected @Nullable List<String> lore;
|
||||
|
||||
public ItemStack(String material, int amount) {
|
||||
this(material, amount, null, null);
|
||||
}
|
||||
|
||||
public ItemStack(String material, int amount, @Nullable String name, @Nullable List<String> lore) {
|
||||
this.material = material;
|
||||
this.amount = amount;
|
||||
this.name = name;
|
||||
this.lore = lore;
|
||||
}
|
||||
|
||||
public String getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<String> getLore() {
|
||||
return lore;
|
||||
}
|
||||
|
||||
public void setMaterial(String material) {
|
||||
this.material = material;
|
||||
}
|
||||
|
||||
public void setAmount(int amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setLore(List<String> lore) {
|
||||
this.lore = lore;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("material", material);
|
||||
if (amount != 1) map.put("amount", amount);
|
||||
if (name != null) map.put("name", name);
|
||||
if (lore != null) map.put("lore", lore);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static ItemStack deserialize(ConfigureSection section) {
|
||||
return new ItemStack(
|
||||
section.getString("material"),
|
||||
section.getInt("amount", 1),
|
||||
section.getString("name"),
|
||||
section.getStringList("lore")
|
||||
);
|
||||
}
|
||||
}
|
||||
+12
-10
@@ -1,26 +1,28 @@
|
||||
package cc.carm.lib.configuration.demo.tests.model;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.source.section.ConfigureSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TestModel extends AbstractModel {
|
||||
public class UserRecord extends AbstractRecord {
|
||||
|
||||
public UUID uuid;
|
||||
public static final UserRecord CARM = new UserRecord("Carm", UUID.fromString("f7b3b3b3-3b3b-3b3b-3b3b-3b3b3b3b3b3b"));
|
||||
|
||||
public TestModel(String name, UUID uuid) {
|
||||
protected UUID uuid;
|
||||
|
||||
public UserRecord(String name, UUID uuid) {
|
||||
super(name);
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public void setUuid(UUID uuid) {
|
||||
public void uuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
public UUID uuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@@ -33,16 +35,16 @@ public class TestModel extends AbstractModel {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static TestModel deserialize(ConfigurationWrapper<?> section) {
|
||||
public static UserRecord deserialize(ConfigureSection section) {
|
||||
String name = section.getString("name");
|
||||
if (name == null) throw new NullPointerException("name is null");
|
||||
String uuidString = section.getString("info.uuid");
|
||||
if (uuidString == null) throw new NullPointerException("uuid is null");
|
||||
return new TestModel(name, UUID.fromString(uuidString));
|
||||
return new UserRecord(name, UUID.fromString(uuidString));
|
||||
}
|
||||
|
||||
public static TestModel random() {
|
||||
return new TestModel(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID());
|
||||
public static UserRecord random() {
|
||||
return new UserRecord(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?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.4</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-commentable</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user