mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 18:48:20 +08:00
Compare commits
199 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c32a941906 | |||
| 8b597d10f9 | |||
| cedfcfd262 | |||
| 12add75232 | |||
| 4d29a24768 | |||
| 8626b86961 | |||
| 754dc1de1f | |||
| 3d1d4ca609 | |||
| 990b10b6f0 | |||
| 7462cd720e | |||
| 6bc83eb3b3 | |||
| dbec551c84 | |||
| 82cca5eca2 | |||
| 65f3cc1b3d | |||
| fae048dd69 | |||
| 854e3df49f | |||
| f6167c3b5e | |||
| ffe3e88b3b | |||
| caa3077f48 | |||
| 05dbf0b504 | |||
| 810e95198e | |||
| 10004f16b4 | |||
| 8e19748c7c | |||
| 17762a2e70 | |||
| 7dbd607a3f | |||
| 035e8a227e | |||
| 04eaf6606d | |||
| e558e93410 | |||
| e55fe3a8d5 | |||
| 8f075b99b5 | |||
| 09a0b3c373 | |||
| 4df4977733 | |||
| 6434feb980 | |||
| bc3e4b3e6f | |||
| c2a9e2254c | |||
| 251dd208af | |||
| afd1bbfc0f | |||
| a699f6c164 | |||
| 844cbfab53 | |||
| f74d5d29f9 | |||
| 6f28abebb9 | |||
| 842cd78ce3 | |||
| 11f1f36a15 | |||
| ee9f29ba30 | |||
| f5bccdaad5 | |||
| f5f70ff69b | |||
| 3e221740bc | |||
| 96d09be977 | |||
| 5d7c946db5 | |||
| 9e008ff4cd | |||
| df19da170b | |||
| 3473ef2247 | |||
| d543530305 | |||
| d81855697c | |||
| 5c16e98f30 | |||
| 3f1ffadeff | |||
| d8191b7c6d | |||
| ba66220f92 | |||
| 730d6d7e9c | |||
| 4523190cb0 | |||
| 2a49e2ee6b | |||
| 77b223b2cb | |||
| 8e7ac263e7 | |||
| 80f03ec501 | |||
| 1c002ae535 | |||
| a4659c5c9f | |||
| 1c27c2f881 | |||
| 476cb79e2f | |||
| bf716b06ae | |||
| 00170e6d77 | |||
| 1bac201427 | |||
| cd77639df5 | |||
| 5ae756929a | |||
| a9e80aecce | |||
| 477f0d11bc | |||
| 53ff38a76f | |||
| c32bea3864 | |||
| 15823bb076 | |||
| f303f56199 | |||
| 22f2674e7c | |||
| 9eb9c9e13d | |||
| 616314c7f0 | |||
| 05f504a347 | |||
| bf6ea97b99 | |||
| 9847399e56 | |||
| 18515d4a78 | |||
| 9f1fc5bf90 | |||
| e7847b2166 | |||
| 69cec281be | |||
| 28dc9b9e55 | |||
| c60ba074d9 | |||
| e88bf301cc | |||
| 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 |
@@ -1,9 +1,3 @@
|
|||||||
# EasyConfiguration Javadoc
|
# configured Javadoc
|
||||||
|
|
||||||
基于 [Github Pages](https://pages.github.com/) 搭建,请访问 [JavaDoc](https://carmjos.github.io/EasyConfiguration) 。
|
Based on [Github Pages](https://pages.github.com/), please see [JavaDoc](https://carmjos.github.io/configured) 。
|
||||||
|
|
||||||
## 如何实现?
|
|
||||||
|
|
||||||
若您也想通过 [Github Actions](https://docs.github.com/en/actions/learn-github-actions)
|
|
||||||
自动部署项目的Javadoc到 [Github Pages](https://pages.github.com/) ,
|
|
||||||
可以参考我的文章 [《自动部署Javadoc到Github Pages》](https://pages.carm.cc/doc/javadoc-in-github.html) 。
|
|
||||||
+1
-16
@@ -1,16 +1 @@
|
|||||||
# 欢迎使用 EasyConfiguration !
|
# Documentation
|
||||||
|
|
||||||
这个项目刚刚创建,详细的Javadoc与开发指南还在补充,请给我一点时间~
|
|
||||||
|
|
||||||
|
|
||||||
## 基本定义
|
|
||||||
|
|
||||||
Value: 实际配置的单例值。
|
|
||||||
|
|
||||||
Manifest: 用于描述值基本配置的对象。
|
|
||||||
|
|
||||||
Provider: 用于提供配置文件的接口。
|
|
||||||
|
|
||||||
Wrapper: 用于包装配置文件的接口。
|
|
||||||
|
|
||||||
Initializer: 用于初始化的接口
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 23 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
@@ -29,11 +29,11 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
# Checkout the repository to the GitHub Actions runner
|
# Checkout the repository to the GitHub Actions runner
|
||||||
- name: Checkout code
|
- 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
|
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
|
||||||
- name: Run Codacy Analysis CLI
|
- name: Run Codacy Analysis CLI
|
||||||
uses: codacy/codacy-analysis-cli-action@d840f886c4bd4edc059706d09c6a1586111c540b
|
uses: codacy/codacy-analysis-cli-action@09916000460adeeedc96b9704f86deba53e2ad5d
|
||||||
with:
|
with:
|
||||||
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
|
# 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
|
# 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
|
# Upload the SARIF file generated in the previous step
|
||||||
- name: Upload SARIF results file
|
- name: Upload SARIF results file
|
||||||
uses: github/codeql-action/upload-sarif@v1
|
uses: github/codeql-action/upload-sarif@v3
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# 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).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v1
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -67,4 +67,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- 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
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: "Set up JDK"
|
- name: "Set up JDK"
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: '11'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
@@ -40,14 +40,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
rm -rf docs
|
rm -rf docs
|
||||||
mkdir -vp docs
|
mkdir -vp docs
|
||||||
cp -vrf core/target/apidocs/* docs/
|
cp -vrf core/target/reports/apidocs/* docs/
|
||||||
cp -vrf .doc/JAVADOC-README.md docs/README.md
|
cp -vrf .doc/JAVADOC-README.md docs/README.md
|
||||||
|
|
||||||
- name: "Generate the Javadoc sitemap"
|
- name: "Generate the Javadoc sitemap"
|
||||||
id: sitemap
|
id: sitemap
|
||||||
uses: cicirello/generate-sitemap@v1
|
uses: cicirello/generate-sitemap@v1
|
||||||
with:
|
with:
|
||||||
base-url-path: https://CarmJos.github.io/EasyConfiguration
|
base-url-path: https://CarmJos.github.io/configured
|
||||||
path-to-root: docs
|
path-to-root: docs
|
||||||
|
|
||||||
- name: "Output stats"
|
- name: "Output stats"
|
||||||
@@ -72,7 +72,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd docs
|
cd docs
|
||||||
git init
|
git init
|
||||||
git remote add origin git@github.com:CarmJos/EasyConfiguration.git
|
git remote add origin git@github.com:CarmJos/configured.git
|
||||||
git checkout -b gh-pages
|
git checkout -b gh-pages
|
||||||
git add -A
|
git add -A
|
||||||
git commit -m "API Document generated."
|
git commit -m "API Document generated."
|
||||||
@@ -87,21 +87,21 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: "Set up JDK"
|
- name: "Set up JDK"
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: '11'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
cache: maven
|
cache: maven
|
||||||
server-id: ossrh
|
server-id: central
|
||||||
server-username: MAVEN_USERNAME
|
server-username: MAVEN_USERNAME
|
||||||
server-password: MAVEN_PASSWORD
|
server-password: MAVEN_PASSWORD
|
||||||
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
|
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
|
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
|
||||||
|
|
||||||
- name: "Central Deploy"
|
- name: "Central Deploy"
|
||||||
run: mvn -B -Possrh deploy --file pom.xml -DskipTests
|
run: mvn -B -Pcentral deploy --file pom.xml -DskipTests
|
||||||
env:
|
env:
|
||||||
MAVEN_USERNAME: ${{ secrets.OSSRH_USER }}
|
MAVEN_USERNAME: ${{ secrets.OSSRH_USER }}
|
||||||
MAVEN_PASSWORD: ${{ secrets.OSSRH_PASS }}
|
MAVEN_PASSWORD: ${{ secrets.OSSRH_PASS }}
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: "Set up JDK"
|
- name: "Set up JDK"
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: '11'
|
java-version: '11'
|
||||||
distribution: 'adopt'
|
distribution: 'adopt'
|
||||||
@@ -26,7 +26,7 @@ jobs:
|
|||||||
- name: "Target Stage"
|
- name: "Target Stage"
|
||||||
run: mkdir staging && cp */target/*.jar staging
|
run: mkdir staging && cp */target/*.jar staging
|
||||||
- name: "Upload artifact"
|
- name: "Upload artifact"
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Artifact
|
name: Artifact
|
||||||
path: staging
|
path: staging
|
||||||
|
|||||||
+2
-1
@@ -1,3 +1,4 @@
|
|||||||
/.idea/
|
.idea/*
|
||||||
|
!.idea/icon.svg
|
||||||
**/target/
|
**/target/
|
||||||
**.iml
|
**.iml
|
||||||
Generated
+36
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 23 KiB |
@@ -1,6 +1,7 @@
|
|||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
Version 3, 29 June 2007
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
|
||||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
of this license document, but changing it is not allowed.
|
of this license document, but changing it is not allowed.
|
||||||
|
|||||||
@@ -1,39 +1,134 @@
|
|||||||
|
<div align=center>
|
||||||
|
<img src=".doc/images/banner.png" alt="Banner"/>
|
||||||
|
|
||||||
|
[](https://github.com/CarmJos/configured/releases)
|
||||||
|
[](https://www.gnu.org/licenses/lgpl-3.0.html)
|
||||||
|
[](https://github.com/CarmJos/configured/actions/workflows/maven.yml)
|
||||||
|
[](https://www.codefactor.io/repository/github/carmjos/configured)
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
README LANGUAGES [ [**English**](README.md) | [中文](README_CN.md) ]
|
README LANGUAGES [ [**English**](README.md) | [中文](README_CN.md) ]
|
||||||
|
</div>
|
||||||
|
|
||||||
# EasyConfiguration
|
# configured _(config framework)_
|
||||||
|
|
||||||
[](https://github.com/CarmJos/EasyConfiguration/releases)
|
<img src=".doc/images/logo-bg.svg" width="150px" alt="logo" align="right" style="float: right"/>
|
||||||
[](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)
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
**Easy _(to make)_ Configurations!**
|
_**"Once set, Simple get."**_
|
||||||
|
|
||||||
A simple, easy-to-use and universal solution for managing configuration files.
|
A simple, easy-to-use and universal solution for managing, loading, reading,
|
||||||
Enjoy the ease of use with customizable formats for loading, reading, and updating your configuration files.
|
and updating configuration files.
|
||||||
|
|
||||||
|
Supported **JSON**, **YAML**, **Hocon**, **TOML**, **SQL**, **MongoDB**... and much more!
|
||||||
|
|
||||||
## Features & Advantages
|
## Features & Advantages
|
||||||
|
|
||||||
- Class-based mechanism for initializing, loading, retrieving, and updating configuration files, ensuring convenience and efficiency.
|
Supported [YAML](impl/yaml), [JSON](impl/json), [HOCON](impl/hocon) and [SQL](impl/sql) based configuration files
|
||||||
|
format.
|
||||||
|
|
||||||
|
- Class-based mechanism for initializing, loading, retrieving, and updating configuration files, ensuring convenience
|
||||||
|
and efficiency.
|
||||||
- Supports manual serialization and deserialization of complex configurations.
|
- Supports manual serialization and deserialization of complex configurations.
|
||||||
- Offers multiple builder forms for rapid construction of `ConfigValue<?>` objects.
|
- Offers multiple builder forms for rapid construction of `ConfigValue<?>` objects.
|
||||||
- Enables specification of configuration paths, comments, and more via annotations.
|
- Enables specification of configuration paths, comments, and more via annotations.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
For the latest JavaDoc release, [CLICK HERE](https://CarmJos.github.io/configured).
|
||||||
|
|
||||||
For a detailed development guide, [CLICK HERE](.doc/README.md).
|
For a detailed development guide, [CLICK HERE](.doc/README.md).
|
||||||
|
|
||||||
For the latest JavaDoc release, [CLICK HERE](https://CarmJos.github.io/EasyConfiguration).
|
### Preview
|
||||||
|
|
||||||
### Code Samples
|
To quickly demonstrate the applicability of the project, here are a few practical demonstrations:
|
||||||
|
|
||||||
Check out some code demonstrations [HERE](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java).
|
- [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).
|
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()); // true
|
||||||
|
SampleConfig.ENABLED.set(false);
|
||||||
|
System.out.println("And now? -> " + SampleConfig.ENABLED.resolve()); // false
|
||||||
|
// 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
|
### Dependencies
|
||||||
|
|
||||||
#### Maven Dependency
|
#### Maven Dependency
|
||||||
@@ -42,6 +137,7 @@ For more examples, see the [Development Guide](.doc/README.md).
|
|||||||
<summary>Remote Repository Configuration</summary>
|
<summary>Remote Repository Configuration</summary>
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
|
|
||||||
<project>
|
<project>
|
||||||
<repositories>
|
<repositories>
|
||||||
|
|
||||||
@@ -54,9 +150,9 @@ For more examples, see the [Development Guide](.doc/README.md).
|
|||||||
|
|
||||||
<repository>
|
<repository>
|
||||||
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
|
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
|
||||||
<id>EasyConfiguration</id>
|
<id>configured</id>
|
||||||
<name>GitHub Packages</name>
|
<name>GitHub Packages</name>
|
||||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
<url>https://maven.pkg.github.com/CarmJos/configured</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
</repositories>
|
</repositories>
|
||||||
@@ -69,12 +165,13 @@ For more examples, see the [Development Guide](.doc/README.md).
|
|||||||
<summary>Generic Native Dependency</summary>
|
<summary>Generic Native Dependency</summary>
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
|
|
||||||
<project>
|
<project>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”. -->
|
<!-- Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”. -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<artifactId>easyconfiguration-core</artifactId>
|
<artifactId>configured-core</artifactId>
|
||||||
<version>[LATEST RELEASE]</version>
|
<version>[LATEST RELEASE]</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -82,15 +179,15 @@ For more examples, see the [Development Guide](.doc/README.md).
|
|||||||
<!-- YAML file-based implementation, compatible with all Java environments. -->
|
<!-- YAML file-based implementation, compatible with all Java environments. -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<artifactId>easyconfiguration-yaml</artifactId>
|
<artifactId>configured-yaml</artifactId>
|
||||||
<version>[LATEST RELEASE]</version>
|
<version>[LATEST RELEASE]</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments. -->
|
<!-- JSON file-based implementation, compatible with all Java environments. -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<artifactId>easyconfiguration-json</artifactId>
|
<artifactId>configured-gson</artifactId>
|
||||||
<version>[LATEST RELEASE]</version>
|
<version>[LATEST RELEASE]</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -113,7 +210,7 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
||||||
// Using GitHub dependencies for real-time updates, configuration required (recommended).
|
// Using GitHub dependencies for real-time updates, configuration required (recommended).
|
||||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
maven { url 'https://maven.pkg.github.com/CarmJos/configured' }
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -128,13 +225,13 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
// Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”.
|
// Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”.
|
||||||
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
|
api "cc.carm.lib:configured-core:[LATEST RELEASE]"
|
||||||
|
|
||||||
// YAML file-based implementation, compatible with all Java environments.
|
// YAML file-based implementation, compatible with all Java environments.
|
||||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
api "cc.carm.lib:configured-yaml:[LATEST RELEASE]"
|
||||||
|
|
||||||
// JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments.
|
// JSON file-based implementation, compatible with all Java environments.
|
||||||
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
|
api "cc.carm.lib:configured-gson:[LATEST RELEASE]"
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -145,10 +242,11 @@ dependencies {
|
|||||||
|
|
||||||
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos)
|
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos)
|
||||||
|
|
||||||
EasyConfiguration for MineCraft!
|
configured for MineCraft!
|
||||||
Easily manage configurations on MineCraft-related server platforms.
|
Easily manage configurations on MineCraft-related server platforms.
|
||||||
|
|
||||||
Currently supports BungeeCord, Bukkit (Spigot) servers, with more platforms to be supported soon.
|
Currently, it supports BungeeCord, Velocity, Bukkit (Spigot) servers,
|
||||||
|
with more platforms to be supported soon.
|
||||||
|
|
||||||
## Support and Donation
|
## Support and Donation
|
||||||
|
|
||||||
@@ -158,9 +256,14 @@ Thank you for supporting open-source projects!
|
|||||||
|
|
||||||
Many thanks to Jetbrains for kindly providing a license for us to work on this and other open-source projects.
|
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/ArtformGames/ResidenceList)
|
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/configured)
|
||||||
|
|
||||||
|
Many thanks to [ArtformGames](https://github.com/ArtformGames) for their
|
||||||
|
strong support and active contribution to this project!
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/ArtformGames/.github/master/logo/logo_full.svg" width="317px" height="117px" alt="ArtformGames">
|
||||||
|
|
||||||
## Open Source License
|
## Open Source License
|
||||||
|
|
||||||
This project's source code is licensed under the [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html).
|
This project's source code is licensed under
|
||||||
|
the [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html).
|
||||||
|
|||||||
+134
-28
@@ -1,28 +1,29 @@
|
|||||||
```text
|
<div align=center>
|
||||||
____ _____ ____ __ _
|
<img src=".doc/images/banner.png" alt="Banner"/>
|
||||||
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
|
|
||||||
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
|
[](https://github.com/CarmJos/configured/releases)
|
||||||
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
|
[](https://www.gnu.org/licenses/lgpl-3.0.html)
|
||||||
/___/ /___/
|
[](https://github.com/CarmJos/configured/actions/workflows/maven.yml)
|
||||||
```
|
[](https://www.codefactor.io/repository/github/carmjos/configured)
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
||||||
|
|
||||||
# EasyConfiguration
|
</div>
|
||||||
|
|
||||||
[](https://github.com/CarmJos/EasyConfiguration/releases)
|
# configured _(配置文件框架)_
|
||||||
[](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)
|
|
||||||

|
|
||||||

|
|
||||||
|
|
||||||
**轻松(做)配置!**
|
<img src=".doc/images/logo-bg.svg" width="150px" alt="logo" align="right" style="float: right"/>
|
||||||
|
|
||||||
|
**一次配置,轻松读取!**
|
||||||
|
|
||||||
一款简单便捷的通用配置文件加载、读取与更新工具,可自定义配置的格式。
|
一款简单便捷的通用配置文件加载、读取与更新工具,可自定义配置的格式。
|
||||||
|
|
||||||
## 特性 & 优势
|
## 特性 & 优势
|
||||||
|
|
||||||
|
支持 [YAML](providers/yaml), [JSON](providers/gson), [HOCON](providers/hocon) 和 [SQL](providers/sql) 等多种配置文件格式。
|
||||||
|
|
||||||
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
|
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
|
||||||
- 支持复杂配置的手动序列化、反序列化。
|
- 支持复杂配置的手动序列化、反序列化。
|
||||||
- 提供多种builder形式,快速构建 `ConfigValue<?>` 对象。
|
- 提供多种builder形式,快速构建 `ConfigValue<?>` 对象。
|
||||||
@@ -30,11 +31,94 @@ README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
|||||||
|
|
||||||
## 开发
|
## 开发
|
||||||
|
|
||||||
详细开发介绍请 [点击这里](.doc/README.md) , JavaDoc(最新Release) 请 [点击这里](https://CarmJos.github.io/EasyConfiguration) 。
|
详细开发介绍请 [点击这里](.doc/README.md) , JavaDoc(最新Release)
|
||||||
|
请 [点击这里](https://CarmJos.github.io/configured) 。
|
||||||
|
|
||||||
### 示例代码
|
### 示例代码
|
||||||
|
|
||||||
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java) 查看部分代码演示,更多演示详见 [开发介绍](.doc/README.md) 。
|
为快速的展示该项目的适用性,这里有几个实际演示:
|
||||||
|
|
||||||
|
- [数据库配置文件实例](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
|
||||||
|
```
|
||||||
|
|
||||||
### 依赖方式
|
### 依赖方式
|
||||||
|
|
||||||
@@ -57,9 +141,9 @@ README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
|||||||
|
|
||||||
<repository>
|
<repository>
|
||||||
<!--采用github依赖库,实时更新,但需要配置 (推荐) -->
|
<!--采用github依赖库,实时更新,但需要配置 (推荐) -->
|
||||||
<id>EasyConfiguration</id>
|
<id>configured</id>
|
||||||
<name>GitHub Packages</name>
|
<name>GitHub Packages</name>
|
||||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
<url>https://maven.pkg.github.com/CarmJos/configured</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
|
||||||
<repository>
|
<repository>
|
||||||
@@ -85,7 +169,7 @@ README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
|||||||
<!--基础实现部分,需要自行实现“Provider”与“Wrapper”。-->
|
<!--基础实现部分,需要自行实现“Provider”与“Wrapper”。-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<artifactId>easyconfiguration-core</artifactId>
|
<artifactId>configured-core</artifactId>
|
||||||
<version>[LATEST RELEASE]</version>
|
<version>[LATEST RELEASE]</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -93,7 +177,7 @@ README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
|||||||
<!--基于YAML文件的实现版本,可用于全部Java环境。-->
|
<!--基于YAML文件的实现版本,可用于全部Java环境。-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<artifactId>easyconfiguration-yaml</artifactId>
|
<artifactId>configured-yaml</artifactId>
|
||||||
<version>[LATEST RELEASE]</version>
|
<version>[LATEST RELEASE]</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -102,7 +186,21 @@ README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
|||||||
<!--需要注意的是,JSON不支持文件注释。-->
|
<!--需要注意的是,JSON不支持文件注释。-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<artifactId>easyconfiguration-json</artifactId>
|
<artifactId>configured-gson</artifactId>
|
||||||
|
<version>[LATEST RELEASE]</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cc.carm.lib</groupId>
|
||||||
|
<artifactId>configured-hocon</artifactId>
|
||||||
|
<version>[LATEST RELEASE]</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>cc.carm.lib</groupId>
|
||||||
|
<artifactId>configured-sql</artifactId>
|
||||||
<version>[LATEST RELEASE]</version>
|
<version>[LATEST RELEASE]</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
@@ -125,7 +223,7 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
||||||
// 采用github依赖库,实时更新,但需要配置 (推荐)
|
// 采用github依赖库,实时更新,但需要配置 (推荐)
|
||||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
maven { url 'https://maven.pkg.github.com/CarmJos/configured' }
|
||||||
|
|
||||||
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
|
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
|
||||||
maven { url 'https://repo.carm.cc/repository/maven-public/' }
|
maven { url 'https://repo.carm.cc/repository/maven-public/' }
|
||||||
@@ -142,14 +240,18 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
//基础实现部分,需要自行实现“Provider”与“Wrapper”。
|
//基础实现部分,需要自行实现“Provider”与“Wrapper”。
|
||||||
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
|
api "cc.carm.lib:configured-core:[LATEST RELEASE]"
|
||||||
|
|
||||||
//基于YAML文件的实现版本,可用于全部Java环境。
|
//基于YAML文件的实现版本,可用于全部Java环境。
|
||||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
api "cc.carm.lib:configured-yaml:[LATEST RELEASE]"
|
||||||
|
|
||||||
//基于JSON文件的实现版本,可用于全部Java环境。
|
//基于JSON文件的实现版本,可用于全部Java环境。
|
||||||
//需要注意的是,JSON不支持文件注释。
|
//需要注意的是,JSON不支持文件注释。
|
||||||
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
|
api "cc.carm.lib:configured-gson:[LATEST RELEASE]"
|
||||||
|
|
||||||
|
api "cc.carm.lib:configured-hocon:[LATEST RELEASE]"
|
||||||
|
|
||||||
|
api "cc.carm.lib:configured-sql:[LATEST RELEASE]"
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -160,7 +262,7 @@ dependencies {
|
|||||||
|
|
||||||
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos )
|
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos )
|
||||||
|
|
||||||
EasyConfiguration for MineCraft!
|
configured for MineCraft!
|
||||||
开始在 MineCraft 相关服务器平台上轻松(做)配置吧!
|
开始在 MineCraft 相关服务器平台上轻松(做)配置吧!
|
||||||
|
|
||||||
目前支持 BungeeCord, Bukkit(Spigot) 服务端,后续将支持更多平台。
|
目前支持 BungeeCord, Bukkit(Spigot) 服务端,后续将支持更多平台。
|
||||||
@@ -173,7 +275,11 @@ EasyConfiguration for MineCraft!
|
|||||||
|
|
||||||
万分感谢 Jetbrains 为我们提供了从事此项目和其他开源项目的许可!
|
万分感谢 Jetbrains 为我们提供了从事此项目和其他开源项目的许可!
|
||||||
|
|
||||||
[](https://www.jetbrains.com/?from=https://github.com/ArtformGames/ResidenceList)
|
[](https://www.jetbrains.com/?from=https://github.com/CarmJos/configured)
|
||||||
|
|
||||||
|
万分感谢来自 [ArtformGames](https://github.com/ArtformGames) 对本项目的大力支持与积极贡献!
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/ArtformGames/.github/master/logo/logo_full.svg" width="317px" height="117px" alt="ArtformGames">
|
||||||
|
|
||||||
## 开源协议
|
## 开源协议
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -3,9 +3,9 @@
|
|||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>easyconfiguration-parent</artifactId>
|
<artifactId>configured-parent</artifactId>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<version>3.9.0</version>
|
<version>4.1.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<properties>
|
<properties>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<artifactId>easyconfiguration-core</artifactId>
|
<artifactId>configured-core</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@@ -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,88 @@
|
|||||||
|
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 @Nullable 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 @Nullable 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,127 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||||
|
return deserialize(holder, ValueType.of(type), source);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
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,20 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value deserializer, convert base data to target value.
|
||||||
|
*
|
||||||
|
* @param <TYPE> The type of target value
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueParser<TYPE> {
|
||||||
|
|
||||||
|
@Nullable TYPE parse(
|
||||||
|
@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@NotNull ValueType<? super TYPE> type, @NotNull Object data
|
||||||
|
) throws Exception;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value serializer, convert target value to base data.
|
||||||
|
*
|
||||||
|
* @param <TYPE> The type of value
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueSerializer<TYPE> {
|
||||||
|
|
||||||
|
@Nullable Object serialize(
|
||||||
|
@NotNull ConfigurationHolder<?> holder,
|
||||||
|
@NotNull ValueType<? super TYPE> type, @NotNull TYPE value
|
||||||
|
) throws Exception;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
package cc.carm.lib.configuration.adapter;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.List;
|
||||||
|
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 @NotNull Class<T> clazz) {
|
||||||
|
return of((Type) clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> ValueType<List<T>> ofList(final @NotNull Class<T> paramType) {
|
||||||
|
return of(List.class, paramType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.getClass().getName());
|
||||||
|
sb.append('<');
|
||||||
|
Type[] args = pt.getActualTypeArguments();
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
if (i > 0) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append(args[i].getTypeName());
|
||||||
|
}
|
||||||
|
sb.append('>');
|
||||||
|
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,38 @@
|
|||||||
|
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 java.util.UUID;
|
||||||
|
|
||||||
|
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<UUID> UUID = new ValueAdapter<>(
|
||||||
|
ValueType.of(UUID.class),
|
||||||
|
(provider, type, value) -> value.toString(),
|
||||||
|
(provider, type, value) -> java.util.UUID.fromString(value.toString())
|
||||||
|
);
|
||||||
|
|
||||||
|
@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,164 @@
|
|||||||
|
package cc.carm.lib.configuration.builder;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.function.DataValidator;
|
||||||
|
import cc.carm.lib.configuration.function.ValueValidator;
|
||||||
|
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.NotNullByDefault;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public abstract class AbstractConfigBuilder<
|
||||||
|
TYPE, UNIT, RESULT extends ConfigValue<TYPE, UNIT>, HOLDER extends ConfigurationHolder<?>,
|
||||||
|
SELF extends AbstractConfigBuilder<TYPE, UNIT, RESULT, HOLDER, SELF>
|
||||||
|
> {
|
||||||
|
|
||||||
|
protected final Class<? super HOLDER> providerClass;
|
||||||
|
protected final ValueType<TYPE> type;
|
||||||
|
|
||||||
|
protected @Nullable HOLDER holder;
|
||||||
|
protected @Nullable String path;
|
||||||
|
|
||||||
|
protected @NotNull ValueValidator<UNIT> valueValidator = ValueValidator.none();
|
||||||
|
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 SELF self();
|
||||||
|
|
||||||
|
public abstract @NotNull RESULT build();
|
||||||
|
|
||||||
|
public SELF holder(@Nullable HOLDER holder) {
|
||||||
|
this.holder = holder;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF path(@Nullable String path) {
|
||||||
|
this.path = path;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link ValueValidator} for the value.
|
||||||
|
*
|
||||||
|
* @param validator The validator to set.
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public SELF validator(@NotNull ValueValidator<UNIT> validator) {
|
||||||
|
this.valueValidator = validator;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link DataValidator} for the value.
|
||||||
|
*
|
||||||
|
* @param validator The validator to set.
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public SELF validator(@NotNull DataValidator<? super UNIT> validator) {
|
||||||
|
return validator((h, value) -> validator.validate(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the value with the specified condition.
|
||||||
|
*
|
||||||
|
* @param validator The validator to append.
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public SELF validate(@NotNull ValueValidator<? super UNIT> validator) {
|
||||||
|
return validator(this.valueValidator.and(validator));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the value with the specified condition.
|
||||||
|
*
|
||||||
|
* @param validator The validator to append.
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public SELF validate(@NotNull DataValidator<? super UNIT> validator) {
|
||||||
|
return validate((h, value) -> validator.validate(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the value with the specified condition.
|
||||||
|
*
|
||||||
|
* @param condition The condition to check, if the condition is false, an exception will be thrown.
|
||||||
|
* @param exception The exception to throw if the condition is false.
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public SELF validate(@NotNull Predicate<? super UNIT> condition, @NotNull Exception exception) {
|
||||||
|
return validate((h, value) -> {
|
||||||
|
if (!condition.test(value)) throw exception;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the value with the specified condition.
|
||||||
|
*
|
||||||
|
* @param condition The condition to check, if the condition is false, an exception will be thrown.
|
||||||
|
* @param msg The message to throw if the condition is false.
|
||||||
|
* @return this builder
|
||||||
|
*/
|
||||||
|
public SELF validate(@NotNull Predicate<? super UNIT> condition, @NotNull String msg) {
|
||||||
|
return validate((h, value) -> {
|
||||||
|
if (!condition.test(value)) throw new IllegalArgumentException(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF initializer(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
|
||||||
|
this.initializer = initializer;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF append(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
|
||||||
|
return initializer(initializer.andThen(initializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF append(@NotNull Consumer<ConfigurationHolder<?>> initializer) {
|
||||||
|
return append((provider, valuePath) -> initializer.accept(provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF defaults(@Nullable TYPE defaultValue) {
|
||||||
|
return defaults(() -> defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) {
|
||||||
|
this.defaultValueSupplier = supplier;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) {
|
||||||
|
return append((h, p) -> metaConsumer.accept(h.metadata(p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public <M> SELF meta(@NotNull ConfigurationMetadata<M> type, @Nullable M value) {
|
||||||
|
return meta(h -> h.set(type, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected @NotNull ValueManifest<TYPE, UNIT> buildManifest() {
|
||||||
|
return new ValueManifest<>(
|
||||||
|
type(), this.defaultValueSupplier, this.valueValidator,
|
||||||
|
this.initializer, this.holder, this.path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
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, UNIT,
|
||||||
|
RESULT extends ConfigValue<TYPE, UNIT>,
|
||||||
|
SELF extends CommonConfigBuilder<TYPE, UNIT, RESULT, SELF>
|
||||||
|
> extends AbstractConfigBuilder<TYPE, UNIT, RESULT, ConfigurationHolder<?>, SELF> {
|
||||||
|
|
||||||
|
protected CommonConfigBuilder(ValueType<TYPE> type) {
|
||||||
|
super(ConfigurationHolder.class, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
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.ValueComposer;
|
||||||
|
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, UNIT,
|
||||||
|
RESULT extends ConfigValue<TYPE, UNIT>,
|
||||||
|
SELF extends AbstractSectionBuilder<TYPE, UNIT, RESULT, SELF>
|
||||||
|
> extends CommonConfigBuilder<TYPE, UNIT, RESULT, SELF> {
|
||||||
|
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<UNIT> paramType;
|
||||||
|
|
||||||
|
protected @NotNull ValueHandler<ConfigureSection, UNIT> parser;
|
||||||
|
protected @NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer;
|
||||||
|
|
||||||
|
protected AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<UNIT> paramType,
|
||||||
|
@NotNull ValueHandler<ConfigureSection, UNIT> parser,
|
||||||
|
@NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer) {
|
||||||
|
super(type);
|
||||||
|
this.paramType = paramType;
|
||||||
|
this.parser = parser;
|
||||||
|
this.serializer = serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF parse(@NotNull DataFunction<ConfigureSection, UNIT> valueParser) {
|
||||||
|
return parse((p, section) -> valueParser.handle(section));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF parse(@NotNull ValueHandler<ConfigureSection, UNIT> valueParser) {
|
||||||
|
this.parser = valueParser;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(@NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer) {
|
||||||
|
this.serializer = serializer;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(@NotNull DataFunction<UNIT, ? extends Map<String, Object>> serializer) {
|
||||||
|
return serialize((p, value) -> serializer.handle(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(@NotNull ValueComposer<Map<String, Object>, UNIT> serializer) {
|
||||||
|
return serialize((h, value) -> {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
serializer.accept(h, map, value);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ValueAdapter<UNIT> 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, UNIT, RESULT extends ConfigValue<V, UNIT>,
|
||||||
|
SELF extends AbstractSourceBuilder<V, SOURCE, UNIT, RESULT, SELF>
|
||||||
|
> extends CommonConfigBuilder<V, UNIT, RESULT, SELF> {
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<SOURCE> sourceType;
|
||||||
|
protected final @NotNull ValueType<UNIT> paramType;
|
||||||
|
protected @NotNull ValueHandler<SOURCE, UNIT> valueParser;
|
||||||
|
protected @NotNull ValueHandler<UNIT, SOURCE> valueSerializer;
|
||||||
|
|
||||||
|
protected AbstractSourceBuilder(@NotNull ValueType<V> type,
|
||||||
|
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<UNIT> paramType,
|
||||||
|
@NotNull ValueHandler<SOURCE, UNIT> parser,
|
||||||
|
@NotNull ValueHandler<UNIT, SOURCE> serializer) {
|
||||||
|
super(type);
|
||||||
|
this.sourceType = sourceType;
|
||||||
|
this.paramType = paramType;
|
||||||
|
this.valueParser = parser;
|
||||||
|
this.valueSerializer = serializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF parse(@NotNull DataFunction<SOURCE, UNIT> parser) {
|
||||||
|
return parse((p, source) -> parser.handle(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF parse(@NotNull ValueHandler<SOURCE, UNIT> parser) {
|
||||||
|
this.valueParser = parser;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(@NotNull ValueHandler<UNIT, SOURCE> serializer) {
|
||||||
|
this.valueSerializer = serializer;
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull SELF serialize(@NotNull DataFunction<UNIT, SOURCE> serializer) {
|
||||||
|
return serialize((p, value) -> serializer.handle(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ValueAdapter<UNIT> 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,63 @@
|
|||||||
|
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.Consumer;
|
||||||
|
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 final @NotNull SectionListBuilder<V> defaults(@NotNull Consumer<List<V>> constructor) {
|
||||||
|
return defaults(() -> {
|
||||||
|
List<V> list = new ArrayList<>();
|
||||||
|
constructor.accept(list);
|
||||||
|
return list;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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,65 @@
|
|||||||
|
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.Consumer;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
public final @NotNull SourceListBuilder<SOURCE, V> defaults(@NotNull Consumer<List<V>> constructor) {
|
||||||
|
return defaults(() -> {
|
||||||
|
List<V> list = new ArrayList<>();
|
||||||
|
constructor.accept(list);
|
||||||
|
return list;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourceListBuilder<SOURCE, V> constructor(@NotNull Supplier<? extends List<V>> constructor) {
|
||||||
|
this.constructor = constructor;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <LIST extends List<V>> SourceListBuilder<SOURCE, V> construct(@NotNull LIST list) {
|
||||||
|
return constructor(() -> list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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,49 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.map;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap(@NotNull Comparator<? super K> comparator) {
|
||||||
|
return constructor(() -> new TreeMap<>(comparator));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
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 SectionMapBuilder<MAP, K, V> defaults(@NotNull K key, @NotNull V value) {
|
||||||
|
return defaults(map -> map.put(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
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,92 @@
|
|||||||
|
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> defaults(@NotNull K key, @NotNull V value) {
|
||||||
|
return defaults(map -> map.put(key, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
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,47 @@
|
|||||||
|
package cc.carm.lib.configuration.builder.value;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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,28 @@
|
|||||||
|
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 Configuration} 中的每个 {@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 Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Class<? extends Configuration> clazz, boolean saveDefaults) {
|
|
||||||
initialize(clazz, saveDefaults, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param clazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
* @param loadSubClasses 是否加载内部子类(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Class<? extends Configuration> 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 Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration config) {
|
|
||||||
initialize(config, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration 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 Configuration 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 (!Configuration.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 Configuration && object instanceof Configuration) {
|
|
||||||
// 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。
|
|
||||||
initializeInstance(
|
|
||||||
(Configuration) 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,8 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root interface of the configuration file interfaces,
|
|
||||||
* which is used to label and record the configuration information.
|
|
||||||
*/
|
|
||||||
public interface Configuration {
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root node of the configuration file class,
|
|
||||||
* which is used to label and record the configuration information.
|
|
||||||
*/
|
|
||||||
public abstract class ConfigurationRoot implements Configuration {
|
|
||||||
}
|
|
||||||
@@ -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,58 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.builder.list;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
|
||||||
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<? super 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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceListBuilder<Map<String, Object>, V> fromMap() {
|
|
||||||
return from(
|
|
||||||
Map.class, obj -> (Map<String, Object>) obj,
|
|
||||||
ConfigDataFunction.required(),
|
|
||||||
ConfigDataFunction.required(),
|
|
||||||
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<? super 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<? super 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,68 +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 SectionMapBuilder<M, K, V> fromSection() {
|
|
||||||
return new SectionMapBuilder<>(
|
|
||||||
supplier,
|
|
||||||
keyClass, ConfigDataFunction.castFromString(keyClass),
|
|
||||||
valueClass, ConfigDataFunction.required(),
|
|
||||||
ConfigDataFunction.castToString(), ConfigDataFunction.required());
|
|
||||||
}
|
|
||||||
|
|
||||||
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,99 +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.source.ConfigurationWrapper;
|
|
||||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
|
||||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSectionMap;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class SectionMapBuilder<M extends Map<K, V>, K, V> extends CommonConfigBuilder<M, SectionMapBuilder<M, 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<V> valueClass;
|
|
||||||
protected @NotNull ConfigDataFunction<ConfigurationWrapper<?>, V> valueParser;
|
|
||||||
|
|
||||||
protected @NotNull ConfigDataFunction<K, String> keySerializer;
|
|
||||||
protected @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer;
|
|
||||||
|
|
||||||
public SectionMapBuilder(@NotNull Supplier<@NotNull M> supplier,
|
|
||||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
|
||||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<ConfigurationWrapper<?>, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
|
||||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer) {
|
|
||||||
this.supplier = supplier;
|
|
||||||
this.keyClass = keyClass;
|
|
||||||
this.keyParser = keyParser;
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.valueParser = valueParser;
|
|
||||||
this.keySerializer = keySerializer;
|
|
||||||
this.valueSerializer = valueSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <MAP extends Map<K, V>> SectionMapBuilder<MAP, K, V> supplier(@NotNull Supplier<MAP> supplier) {
|
|
||||||
return new SectionMapBuilder<>(supplier,
|
|
||||||
keyClass, keyParser, valueClass, valueParser, keySerializer, valueSerializer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> defaults(@NotNull Consumer<M> factory) {
|
|
||||||
M map = supplier.get();
|
|
||||||
factory.accept(map);
|
|
||||||
return defaults(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> parseKey(@NotNull ConfigDataFunction<String, K> parser) {
|
|
||||||
this.keyParser = parser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> parseValue(@NotNull ConfigDataFunction<ConfigurationWrapper<?>, V> parser) {
|
|
||||||
this.valueParser = parser;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> serializeKey(@NotNull ConfigDataFunction<K, String> serializer) {
|
|
||||||
this.keySerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> serializeValue(@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
|
||||||
this.valueSerializer = serializer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SectionMapBuilder<M, K, V> serializeValue(@NotNull BiConsumer<V, Map<String, Object>> serializer) {
|
|
||||||
return serializeValue(v -> {
|
|
||||||
Map<String, Object> map = new LinkedHashMap<>();
|
|
||||||
serializer.accept(v, map);
|
|
||||||
return map;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected @NotNull SectionMapBuilder<M, K, V> getThis() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NotNull ConfiguredSectionMap<K, V> build() {
|
|
||||||
return new ConfiguredSectionMap<>(
|
|
||||||
new ValueManifest<>(provider, path, headerComments, inlineComment, defaultValue),
|
|
||||||
this.supplier, this.keyClass, this.keyParser,
|
|
||||||
this.valueClass, this.valueParser,
|
|
||||||
this.keySerializer, this.valueSerializer
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-64
@@ -1,64 +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),
|
|
||||||
s -> {
|
|
||||||
if (s instanceof Enum<?>) return ((Enum<?>) s).name();
|
|
||||||
else return s;
|
|
||||||
}, ConfigDataFunction.toObject()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull SourceValueBuilder<String, V> fromString() {
|
|
||||||
return from(
|
|
||||||
String.class, ConfigDataFunction.castToString(),
|
|
||||||
ConfigValueParser.parseString(this.valueClass),
|
|
||||||
ConfigDataFunction.castToString(), ConfigDataFunction.toObject()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-54
@@ -1,54 +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(ConfigDataFunction<ConfigurationWrapper<?>, V> valueParser) {
|
|
||||||
return parseValue((section, path) -> valueParser.parse(section));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-70
@@ -1,70 +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(ConfigDataFunction<S, V> valueParser) {
|
|
||||||
return parseValue((section, path) -> valueParser.parse(section));
|
|
||||||
}
|
|
||||||
|
|
||||||
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,147 +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) || long.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = longValue().parse(input, (Long) defaultValue);
|
|
||||||
} else if (Integer.class.isAssignableFrom(valueClass) || int.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = intValue().parse(input, (Integer) defaultValue);
|
|
||||||
} else if (Float.class.isAssignableFrom(valueClass) || float.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = floatValue().parse(input, (Float) defaultValue);
|
|
||||||
} else if (Double.class.isAssignableFrom(valueClass) || double.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = doubleValue().parse(input, (Double) defaultValue);
|
|
||||||
} else if (Byte.class.isAssignableFrom(valueClass) || byte.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = byteValue().parse(input, (Byte) defaultValue);
|
|
||||||
} else if (Short.class.isAssignableFrom(valueClass) || short.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = shortValue().parse(input, (Short) defaultValue);
|
|
||||||
}
|
|
||||||
} else if (Boolean.class.isAssignableFrom(valueClass) || boolean.class.isAssignableFrom(valueClass)) {
|
|
||||||
input = booleanValue().parse(input, (Boolean) defaultValue);
|
|
||||||
} else if (Enum.class.isAssignableFrom(valueClass) && input instanceof String) {
|
|
||||||
String enumName = (String) input;
|
|
||||||
input = valueClass.getDeclaredMethod("valueOf", String.class).invoke(null, enumName);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Contract(pure = true)
|
|
||||||
static @NotNull <E extends Enum<E>> ConfigValueParser<Object, E> enumValue(Class<E> enumClass) {
|
|
||||||
return (input, defaultValue) -> {
|
|
||||||
if (input instanceof Enum) {
|
|
||||||
return enumClass.cast(input);
|
|
||||||
} else if (input instanceof String) {
|
|
||||||
return Enum.valueOf(enumClass, (String) input);
|
|
||||||
} else if (input instanceof Number) {
|
|
||||||
return enumClass.getEnumConstants()[((Number) input).intValue()];
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Cannot cast value to " + enumClass.getName());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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,151 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.source;
|
|
||||||
|
|
||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
|
||||||
import cc.carm.lib.configuration.core.Configuration;
|
|
||||||
import cc.carm.lib.configuration.core.Configuration;
|
|
||||||
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 Configuration} 。
|
|
||||||
*/
|
|
||||||
public void initialize(Class<? extends Configuration> configClazz) {
|
|
||||||
initialize(configClazz, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param configClazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(Class<? extends Configuration> configClazz, boolean saveDefaults) {
|
|
||||||
this.getInitializer().initialize(configClazz, saveDefaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定类的所有 {@link ConfigValue} 对象。
|
|
||||||
*
|
|
||||||
* @param configClazz 配置文件类,须继承于 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
* @param loadSubClasses 是否加载内部子类(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(Class<? extends Configuration> configClazz, boolean saveDefaults, boolean loadSubClasses) {
|
|
||||||
this.getInitializer().initialize(configClazz, saveDefaults, loadSubClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration config) {
|
|
||||||
this.getInitializer().initialize(config, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
|
||||||
*
|
|
||||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
|
||||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
|
||||||
*/
|
|
||||||
public void initialize(@NotNull Configuration config, boolean saveDefaults) {
|
|
||||||
this.getInitializer().initialize(config, saveDefaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,194 +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);
|
|
||||||
}
|
|
||||||
|
|
||||||
default <V> @NotNull List<V> getList(@NotNull String path, @NotNull ConfigValueParser<Object, V> parser) {
|
|
||||||
return parseList(getWrapper().getList(path), parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<String> getStringList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.castToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Integer> getIntegerList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.intValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Long> getLongList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.longValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Double> getDoubleList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.doubleValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Float> getFloatList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.floatValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Byte> getByteList(@NotNull String path) {
|
|
||||||
return getList(path, ConfigValueParser.byteValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unmodifiable
|
|
||||||
default @NotNull List<Character> getCharList(@NotNull String path) {
|
|
||||||
return 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.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
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 (IllegalArgumentException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void saveResource(@NotNull String resourcePath, boolean replace)
|
|
||||||
throws IOException, IllegalArgumentException {
|
|
||||||
Objects.requireNonNull(resourcePath, "ResourcePath cannot be null");
|
|
||||||
if (resourcePath.isEmpty()) throw new IllegalArgumentException("ResourcePath cannot be empty");
|
|
||||||
|
|
||||||
resourcePath = resourcePath.replace('\\', '/');
|
|
||||||
|
|
||||||
URL url = this.getClass().getClassLoader().getResource(resourcePath);
|
|
||||||
if (url == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists");
|
|
||||||
|
|
||||||
File outDir = file.getParentFile();
|
|
||||||
|
|
||||||
if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir);
|
|
||||||
if (!file.exists() || replace) {
|
|
||||||
try (OutputStream out = Files.newOutputStream(file.toPath())) {
|
|
||||||
URLConnection connection = url.openConnection();
|
|
||||||
connection.setUseCaches(false);
|
|
||||||
try (InputStream in = connection.getInputStream()) {
|
|
||||||
byte[] buf = new byte[1024];
|
|
||||||
int len;
|
|
||||||
while ((len = in.read(buf)) > 0) {
|
|
||||||
out.write(buf, 0, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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,214 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.impl;
|
|
||||||
|
|
||||||
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 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 abstract class ConfigValueMap<K, V, S> 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<? super S> sourceClass;
|
|
||||||
protected final @NotNull Class<K> keyClass;
|
|
||||||
protected final @NotNull Class<V> valueClass;
|
|
||||||
|
|
||||||
protected final @NotNull ConfigDataFunction<String, K> keyParser;
|
|
||||||
protected final @NotNull ConfigDataFunction<S, V> valueParser;
|
|
||||||
|
|
||||||
protected final @NotNull ConfigDataFunction<K, String> keySerializer;
|
|
||||||
protected final @NotNull ConfigDataFunction<V, Object> valueSerializer;
|
|
||||||
|
|
||||||
|
|
||||||
protected ConfigValueMap(@NotNull ValueManifest<Map<K, V>> manifest, @NotNull Class<? super S> sourceClass,
|
|
||||||
@NotNull Supplier<? extends Map<K, V>> mapObjSupplier,
|
|
||||||
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
|
|
||||||
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
|
||||||
@NotNull ConfigDataFunction<V, Object> valueSerializer) {
|
|
||||||
super(manifest);
|
|
||||||
this.supplier = mapObjSupplier;
|
|
||||||
this.sourceClass = sourceClass;
|
|
||||||
this.keyClass = keyClass;
|
|
||||||
this.valueClass = valueClass;
|
|
||||||
this.keyParser = keyParser;
|
|
||||||
this.valueParser = valueParser;
|
|
||||||
this.keySerializer = keySerializer;
|
|
||||||
this.valueSerializer = valueSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull Class<? super S> getSourceClass() {
|
|
||||||
return sourceClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<S, V> getValueParser() {
|
|
||||||
return valueParser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigDataFunction<K, String> getKeySerializer() {
|
|
||||||
return keySerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public @NotNull ConfigDataFunction<V, Object> getValueSerializer() {
|
|
||||||
return valueSerializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract S getSource(ConfigurationWrapper<?> section, String dataKey);
|
|
||||||
|
|
||||||
@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) {
|
|
||||||
S dataVal = getSource(section, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public V getNotNull(Object key) {
|
|
||||||
return Objects.requireNonNull(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,220 +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<>());
|
|
||||||
// Data that is outdated and needs to be parsed again.
|
|
||||||
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 @NotNull List<V> copy() {
|
|
||||||
return new ArrayList<>(get());
|
|
||||||
}
|
|
||||||
|
|
||||||
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,28 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.type;
|
|
||||||
|
|
||||||
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.ConfigValueMap;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ConfiguredMap<K, V> extends ConfigValueMap<K, V, Object> {
|
|
||||||
|
|
||||||
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, Object.class, mapObjSupplier, keyClass, keyParser, valueClass, valueParser, keySerializer, valueSerializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getSource(ConfigurationWrapper<?> section, String dataKey) {
|
|
||||||
return section.get(dataKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,97 +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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's type class
|
|
||||||
*/
|
|
||||||
public @NotNull Class<V> getValueClass() {
|
|
||||||
return valueClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's parser, cast value from section.
|
|
||||||
*/
|
|
||||||
public @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> getParser() {
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's serializer, serialize value to section.
|
|
||||||
*/
|
|
||||||
public @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> getSerializer() {
|
|
||||||
return serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Get the value that parsed from the configuration section.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public @Nullable V get() {
|
|
||||||
if (!isExpired()) return getCachedOrDefault();
|
|
||||||
// Data that is outdated and needs to be parsed again.
|
|
||||||
|
|
||||||
ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath());
|
|
||||||
if (section == null) return getDefaultValue();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// If there are no errors, update the cache and return.
|
|
||||||
return updateCache(this.parser.parse(section, this.defaultValue));
|
|
||||||
} catch (Exception e) {
|
|
||||||
// There was a parsing error, prompted and returned the default value.
|
|
||||||
e.printStackTrace();
|
|
||||||
return getDefaultValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use the specified value to update the configuration section.
|
|
||||||
* Will use {@link #getSerializer()} to serialize the value to section.
|
|
||||||
*
|
|
||||||
* @param value The value that needs to be set in the configuration.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void set(V value) {
|
|
||||||
updateCache(value);
|
|
||||||
if (value == null) setValue(null);
|
|
||||||
else {
|
|
||||||
try {
|
|
||||||
setValue(serializer.parse(value));
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
-32
@@ -1,32 +0,0 @@
|
|||||||
package cc.carm.lib.configuration.core.value.type;
|
|
||||||
|
|
||||||
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.ConfigValueMap;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class ConfiguredSectionMap<K, V> extends ConfigValueMap<K, V, ConfigurationWrapper<?>> {
|
|
||||||
|
|
||||||
public ConfiguredSectionMap(@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<ConfigurationWrapper<?>, V> valueParser,
|
|
||||||
@NotNull ConfigDataFunction<K, String> keySerializer,
|
|
||||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer) {
|
|
||||||
super(
|
|
||||||
manifest, ConfigurationWrapper.class, mapObjSupplier,
|
|
||||||
keyClass, keyParser, valueClass, valueParser,
|
|
||||||
keySerializer, valueSerializer.andThen(s -> (Object) s)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ConfigurationWrapper<?> getSource(ConfigurationWrapper<?> section, String dataKey) {
|
|
||||||
return section.getConfigurationSection(dataKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,103 +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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <V> ConfiguredValue<V> of(@NotNull V defaultValue) {
|
|
||||||
return of((Class<V>) defaultValue.getClass(), defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's type class
|
|
||||||
*/
|
|
||||||
public @NotNull Class<V> getValueClass() {
|
|
||||||
return valueClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's parser, cast value from base object.
|
|
||||||
*/
|
|
||||||
public @NotNull ConfigValueParser<Object, V> getParser() {
|
|
||||||
return parser;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Value's serializer, serialize value to base object.
|
|
||||||
*/
|
|
||||||
public @NotNull ConfigDataFunction<V, Object> getSerializer() {
|
|
||||||
return serializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V get() {
|
|
||||||
if (!isExpired()) return getCachedOrDefault();
|
|
||||||
// Data that is outdated and needs to be parsed again.
|
|
||||||
|
|
||||||
Object value = getValue();
|
|
||||||
if (value == null) return getDefaultValue(); // 获取的值不存在,直接使用默认值。
|
|
||||||
try {
|
|
||||||
// If there are no errors, update the cache and return.
|
|
||||||
return updateCache(this.parser.parse(value, this.defaultValue));
|
|
||||||
} catch (Exception e) {
|
|
||||||
// There was a parsing error, prompted and returned the default value.
|
|
||||||
e.printStackTrace();
|
|
||||||
return getDefaultValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value of the configuration path.
|
|
||||||
* Will use {@link #getSerializer()} to serialize the value.
|
|
||||||
*
|
|
||||||
* @param value The value to be set
|
|
||||||
*/
|
|
||||||
@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,22 @@
|
|||||||
|
package cc.carm.lib.configuration.function;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ConfigExceptionHandler {
|
||||||
|
|
||||||
|
void handle(@NotNull String path, @NotNull Throwable throwable);
|
||||||
|
|
||||||
|
static @NotNull ConfigExceptionHandler silence() {
|
||||||
|
return (path, throwable) -> {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static @NotNull ConfigExceptionHandler print() {
|
||||||
|
return (path, throwable) -> {
|
||||||
|
System.err.println("Error occurred at path: " + path);
|
||||||
|
throwable.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);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+20
-20
@@ -1,4 +1,4 @@
|
|||||||
package cc.carm.lib.configuration.core.function;
|
package cc.carm.lib.configuration.function;
|
||||||
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Contract;
|
import org.jetbrains.annotations.Contract;
|
||||||
@@ -7,39 +7,39 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@FunctionalInterface
|
@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);
|
Objects.requireNonNull(after);
|
||||||
return data -> after.parse(parse(data));
|
return data -> after.handle(handle(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T> @NotNull ConfigDataFunction<T, T> identity() {
|
static <T> @NotNull DataFunction<T, T> identity() {
|
||||||
return input -> input;
|
return input -> input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T> @NotNull ConfigDataFunction<T, T> identity(Class<T> type) {
|
static <T> @NotNull DataFunction<T, T> identity(Class<T> type) {
|
||||||
return input -> input;
|
return input -> input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T, V> @NotNull ConfigDataFunction<T, V> required() {
|
static <T, V> @NotNull DataFunction<T, V> required() {
|
||||||
return input -> {
|
return input -> {
|
||||||
throw new IllegalArgumentException("Please specify the value parser.");
|
throw new IllegalArgumentException("Please specify the value parser.");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T> @NotNull ConfigDataFunction<T, Object> toObject() {
|
static <T> @NotNull DataFunction<T, Object> toObject() {
|
||||||
return input -> input;
|
return input -> input;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <V> @NotNull ConfigDataFunction<Object, V> castObject(Class<V> valueClass) {
|
static <V> @NotNull DataFunction<Object, V> castObject(Class<V> valueClass) {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||||
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
||||||
@@ -47,7 +47,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <V> @NotNull ConfigDataFunction<String, V> castFromString(Class<V> valueClass) {
|
static <V> @NotNull DataFunction<String, V> castFromString(Class<V> valueClass) {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||||
@@ -55,7 +55,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <T> @NotNull ConfigDataFunction<T, String> castToString() {
|
static <T> @NotNull DataFunction<T, String> castToString() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof String) return (String) input;
|
if (input instanceof String) return (String) input;
|
||||||
else if (input instanceof Enum<?>) return ((Enum<?>) input).name();
|
else if (input instanceof Enum<?>) return ((Enum<?>) input).name();
|
||||||
@@ -64,7 +64,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static <V> @NotNull ConfigDataFunction<String, V> parseString(Class<V> valueClass) {
|
static <V> @NotNull DataFunction<String, V> parseString(Class<V> valueClass) {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||||
@@ -72,7 +72,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Integer> intValue() {
|
static @NotNull DataFunction<Object, Integer> intValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Integer) {
|
if (input instanceof Integer) {
|
||||||
return (Integer) input;
|
return (Integer) input;
|
||||||
@@ -83,7 +83,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Short> shortValue() {
|
static @NotNull DataFunction<Object, Short> shortValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Short) {
|
if (input instanceof Short) {
|
||||||
return (Short) input;
|
return (Short) input;
|
||||||
@@ -94,7 +94,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Double> doubleValue() {
|
static @NotNull DataFunction<Object, Double> doubleValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Double) {
|
if (input instanceof Double) {
|
||||||
return (Double) input;
|
return (Double) input;
|
||||||
@@ -105,7 +105,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Byte> byteValue() {
|
static @NotNull DataFunction<Object, Byte> byteValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Byte) {
|
if (input instanceof Byte) {
|
||||||
return (Byte) input;
|
return (Byte) input;
|
||||||
@@ -116,7 +116,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Float> floatValue() {
|
static @NotNull DataFunction<Object, Float> floatValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Float) {
|
if (input instanceof Float) {
|
||||||
return (Float) input;
|
return (Float) input;
|
||||||
@@ -127,7 +127,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Long> longValue() {
|
static @NotNull DataFunction<Object, Long> longValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Long) {
|
if (input instanceof Long) {
|
||||||
return (Long) input;
|
return (Long) input;
|
||||||
@@ -138,7 +138,7 @@ public interface ConfigDataFunction<T, R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Contract(pure = true)
|
@Contract(pure = true)
|
||||||
static @NotNull ConfigDataFunction<Object, Boolean> booleanValue() {
|
static @NotNull DataFunction<Object, Boolean> booleanValue() {
|
||||||
return input -> {
|
return input -> {
|
||||||
if (input instanceof Boolean) {
|
if (input instanceof Boolean) {
|
||||||
return (Boolean) input;
|
return (Boolean) input;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package cc.carm.lib.configuration.function;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface DataValidator<T> {
|
||||||
|
|
||||||
|
void validate(@Nullable T value) throws Exception;
|
||||||
|
|
||||||
|
default DataValidator<T> compose(DataValidator<? super T> other) {
|
||||||
|
return value -> {
|
||||||
|
validate(value);
|
||||||
|
other.validate(value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package cc.carm.lib.configuration.function;
|
||||||
|
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueComposer<T, U> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept the value and the data, and then compose the value.
|
||||||
|
*
|
||||||
|
* @param holder The configuration holder
|
||||||
|
* @param type The value type, e.g. {@link java.util.List}, {@link java.util.Map}, etc.
|
||||||
|
* @param data The unit data
|
||||||
|
* @throws Exception If an error occurs
|
||||||
|
*/
|
||||||
|
void accept(@NotNull ConfigurationHolder<?> holder, @NotNull T type, @NotNull U data) throws Exception;
|
||||||
|
|
||||||
|
default ValueComposer<T, U> andThen(ValueComposer<? super T, ? super U> 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,47 @@
|
|||||||
|
package cc.carm.lib.configuration.function;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ValueValidator<T> {
|
||||||
|
|
||||||
|
void validate(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception;
|
||||||
|
|
||||||
|
default ValueValidator<T> and(ValueValidator<? super T> other) {
|
||||||
|
return (holder, value) -> {
|
||||||
|
validate(holder, value);
|
||||||
|
other.validate(holder, value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V> ValueValidator<V> none() {
|
||||||
|
return (holder, data) -> {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V> ValueValidator<V> nonnull() {
|
||||||
|
return nonnull("Value cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V> ValueValidator<V> nonnull(String message) {
|
||||||
|
return (holder, data) -> {
|
||||||
|
if (data == null) throw new IllegalArgumentException(message);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V extends Number> ValueValidator<V> range(V min, V max) {
|
||||||
|
return range(min, max, "Value must be in range [" + min + ", " + max + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V extends Number> ValueValidator<V> range(V min, V max, String message) {
|
||||||
|
return (holder, data) -> {
|
||||||
|
if (data.doubleValue() < min.doubleValue() || data.doubleValue() > max.doubleValue()) {
|
||||||
|
throw new IllegalArgumentException(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
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.ConfigExceptionHandler;
|
||||||
|
import cc.carm.lib.configuration.function.DataFunction;
|
||||||
|
import cc.carm.lib.configuration.function.ValueValidator;
|
||||||
|
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 @NotNull ValueAdapterRegistry adapters = new ValueAdapterRegistry();
|
||||||
|
protected @NotNull ConfigurationOptionHolder options = new ConfigurationOptionHolder();
|
||||||
|
protected @NotNull Map<String, ConfigurationMetaHolder> metadata = new HashMap<>();
|
||||||
|
protected @NotNull ConfigurationInitializer initializer = new ConfigurationInitializer();
|
||||||
|
protected @NotNull ConfigExceptionHandler exceptionHandler = ConfigExceptionHandler.print();
|
||||||
|
|
||||||
|
protected ConfigurationFactory() {
|
||||||
|
this.adapters.register(StandardAdapters.PRIMITIVES);
|
||||||
|
this.adapters.register(StandardAdapters.SECTIONS);
|
||||||
|
this.adapters.register(StandardAdapters.ENUMS);
|
||||||
|
this.adapters.register(StandardAdapters.UUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SELF exceptionally(@NotNull ConfigExceptionHandler handler) {
|
||||||
|
this.exceptionHandler = handler;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new annotation for {@link ValueValidator} to the configuration loader
|
||||||
|
*
|
||||||
|
* @param annotation The {@link Annotation}
|
||||||
|
* @param builder The {@link Function} to build the {@link ValueValidator} from the annotation
|
||||||
|
* @param <A> The annotation type
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
public <A extends Annotation> SELF validAnnotation(@NotNull Class<A> annotation,
|
||||||
|
@NotNull Function<A, ValueValidator<Object>> builder) {
|
||||||
|
return initializer(loader -> loader.registerValidAnnotation(annotation, builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the configuration holder.
|
||||||
|
*
|
||||||
|
* @return The configuration holder
|
||||||
|
*/
|
||||||
|
public abstract @NotNull HOLDER build();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
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.function.ConfigExceptionHandler;
|
||||||
|
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.meta.StandardMeta;
|
||||||
|
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.ConfigValue;
|
||||||
|
import cc.carm.lib.configuration.value.ValueManifest;
|
||||||
|
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;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
protected @NotNull ConfigExceptionHandler exceptionHandler;
|
||||||
|
|
||||||
|
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
|
||||||
|
@NotNull ConfigurationOptionHolder options,
|
||||||
|
@NotNull Map<String, ConfigurationMetaHolder> metadata,
|
||||||
|
@NotNull ConfigurationInitializer initializer) {
|
||||||
|
this(adapters, options, metadata, initializer, ConfigExceptionHandler.print());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
|
||||||
|
@NotNull ConfigurationOptionHolder options,
|
||||||
|
@NotNull Map<String, ConfigurationMetaHolder> metadata,
|
||||||
|
@NotNull ConfigurationInitializer initializer,
|
||||||
|
@NotNull ConfigExceptionHandler exceptionHandler) {
|
||||||
|
this.initializer = initializer;
|
||||||
|
this.adapters = adapters;
|
||||||
|
this.options = options;
|
||||||
|
this.metadata = metadata;
|
||||||
|
this.exceptionHandler = exceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
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> @NotNull 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) {
|
||||||
|
return extractMetadata(type, Objects::nonNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type, @NotNull Predicate<@Nullable M> filter) {
|
||||||
|
Map<String, M> metas = new LinkedHashMap<>();
|
||||||
|
for (Map.Entry<String, ConfigurationMetaHolder> entry : this.metadata.entrySet()) {
|
||||||
|
M data = entry.getValue().get(type);
|
||||||
|
if (filter.test(data)) metas.put(entry.getKey(), data);
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableMap(metas);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
public Map<String, ConfigValue<?, ?>> registeredValues() {
|
||||||
|
return extractMetadata(StandardMeta.VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueAdapterRegistry adapters() {
|
||||||
|
return this.adapters;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationInitializer initializer() {
|
||||||
|
return initializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public <T> T deserialize(@NotNull Class<T> type, @Nullable Object source) throws Exception {
|
||||||
|
return adapters().deserialize(this, type, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public <T> T deserialize(@NotNull ValueType<T> type, @Nullable Object source) throws Exception {
|
||||||
|
return adapters().deserialize(this, type, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
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) {
|
||||||
|
throwing(configClass.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(@NotNull Configuration config) {
|
||||||
|
try {
|
||||||
|
initializer.initialize(this, config);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throwing(config.getClass().getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(@NotNull ValueManifest<?, ?> value) {
|
||||||
|
value.holder(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void throwing(@NotNull String path, @NotNull Throwable e) {
|
||||||
|
this.exceptionHandler.handle(path, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exceptionally(@NotNull ConfigExceptionHandler handler) {
|
||||||
|
this.exceptionHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
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, V> {
|
||||||
|
|
||||||
|
static <T, V> ConfigInitializeHandler<T, V> start() {
|
||||||
|
return (provider, path, value, instance) -> {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void whenInitialize(@NotNull ConfigurationHolder<?> holder, @Nullable String path,
|
||||||
|
@NotNull T value, @Nullable V instance) throws Exception;
|
||||||
|
|
||||||
|
default ConfigInitializeHandler<T, V> andThen(ConfigInitializeHandler<T, V> after) {
|
||||||
|
return (provider, path, value, instance) -> {
|
||||||
|
whenInitialize(provider, path, value, instance);
|
||||||
|
after.whenInitialize(provider, path, value, instance);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
default ConfigInitializeHandler<T, V> compose(ConfigInitializeHandler<T, V> before) {
|
||||||
|
return (provider, path, value, instance) -> {
|
||||||
|
before.whenInitialize(provider, path, value, instance);
|
||||||
|
whenInitialize(provider, path, value, instance);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+209
@@ -0,0 +1,209 @@
|
|||||||
|
package cc.carm.lib.configuration.source.loader;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.Configuration;
|
||||||
|
import cc.carm.lib.configuration.function.ValueValidator;
|
||||||
|
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, ConfigValue<?, ?>> valueInitializer;
|
||||||
|
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer;
|
||||||
|
|
||||||
|
public ConfigurationInitializer() {
|
||||||
|
this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationInitializer(@NotNull PathGenerator pathGenerator,
|
||||||
|
@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> valueInitializer,
|
||||||
|
@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
|
||||||
|
this.pathGenerator = pathGenerator;
|
||||||
|
this.valueInitializer = valueInitializer;
|
||||||
|
this.classInitializer = classInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pathGenerator(@NotNull PathGenerator pathGenerator) {
|
||||||
|
this.pathGenerator = pathGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull PathGenerator pathGenerator() {
|
||||||
|
return pathGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer() {
|
||||||
|
return valueInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer) {
|
||||||
|
this.valueInitializer = fieldInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer() {
|
||||||
|
return classInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void classInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
|
||||||
|
this.classInitializer = classInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer) {
|
||||||
|
this.valueInitializer = this.valueInitializer.andThen(fieldInitializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void appendClassInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> 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, instance) -> {
|
||||||
|
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, instance) -> {
|
||||||
|
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 <A extends Annotation> void registerValidAnnotation(@NotNull Class<A> annotation,
|
||||||
|
@NotNull Function<A, ValueValidator<Object>> builder) {
|
||||||
|
appendFieldInitializer((holder, path, field, instance) -> {
|
||||||
|
A data = field.getAnnotation(annotation);
|
||||||
|
if (data == null) return;
|
||||||
|
instance.validate((h, t) -> builder.apply(data).validate(h, t));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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(), root);
|
||||||
|
} catch (Exception e) {
|
||||||
|
holder.throwing(path, e);
|
||||||
|
}
|
||||||
|
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, configField);
|
||||||
|
} catch (Exception e) {
|
||||||
|
holder.throwing(path, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.VALUE, value); // Mark the minimal config value unit.
|
||||||
|
if (holder.option(StandardOptions.SET_DEFAULTS)) {
|
||||||
|
value.setDefault(); // Set default value.
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.valueInitializer.whenInitialize(holder, path, field, value);
|
||||||
|
} catch (Exception e) {
|
||||||
|
holder.throwing(path, e);
|
||||||
|
}
|
||||||
|
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,12 @@
|
|||||||
|
package cc.carm.lib.configuration.source.meta;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.value.ConfigValue;
|
||||||
|
|
||||||
|
public interface StandardMeta {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To mark the {@link ConfigValue} instance of specific path.
|
||||||
|
*/
|
||||||
|
ConfigurationMetadata<ConfigValue<?, ?>> VALUE = ConfigurationMetadata.of();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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,35 @@
|
|||||||
|
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,
|
||||||
|
* may good to set true if you want to keep the config format.
|
||||||
|
* <br> if true, the values will be parsed when
|
||||||
|
* {@link ConfigurationHolder#initialize(Configuration)}.
|
||||||
|
* <br> if false, the values will be parsed when calling
|
||||||
|
* {@link cc.carm.lib.configuration.value.ConfigValue#get()}
|
||||||
|
*/
|
||||||
|
ConfigurationOption<Boolean> PRELOAD = of(true);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,903 @@
|
|||||||
|
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.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current section's path from {@link #parent()} of this section.
|
||||||
|
*
|
||||||
|
* @return The current path of this section, if {@link #isRoot()}, return empty string.
|
||||||
|
*/
|
||||||
|
@NotNull String path();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the full path of this section.
|
||||||
|
*
|
||||||
|
* @return The full path of this section, if {@link #isRoot()}, return empty string.
|
||||||
|
*/
|
||||||
|
default @NotNull String fullPath() {
|
||||||
|
if (parent() == null) return "";
|
||||||
|
return (parent().isRoot() ? "" : parent().fullPath() + pathSeparator()) + path();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the path separator for the section.
|
||||||
|
*
|
||||||
|
* @return The path separator
|
||||||
|
*/
|
||||||
|
default char pathSeparator() {
|
||||||
|
return '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if this section is a root section.
|
||||||
|
*
|
||||||
|
* @return True if this section is a root section, false otherwise.
|
||||||
|
*/
|
||||||
|
@Contract(pure = true)
|
||||||
|
default boolean isRoot() {
|
||||||
|
return parent() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets if this section is empty.
|
||||||
|
*
|
||||||
|
* @return True if this section is empty, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean isEmpty() {
|
||||||
|
return getValues(false).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of keys in this section.
|
||||||
|
*
|
||||||
|
* @return Number of keys in this section.
|
||||||
|
*/
|
||||||
|
default int size(boolean deep) {
|
||||||
|
return getKeys(deep).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set containing all keys in this section.
|
||||||
|
* <p>
|
||||||
|
* If deep is set to true, then this will contain all the keys within any
|
||||||
|
* child {@link ConfigureSection}s (and their children paths).
|
||||||
|
* <p>
|
||||||
|
* If deep is set to false, then this will contain only the keys of any
|
||||||
|
* direct children, and not their own children.
|
||||||
|
*
|
||||||
|
* @param deep Whether to get a deep list.
|
||||||
|
* @return Set of keys contained within this Section.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
default Set<String> getKeys(boolean deep) {
|
||||||
|
return getValues(deep).keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set containing all primary keys in this section.
|
||||||
|
*
|
||||||
|
* @return Set of keys contained within this Section.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
default Set<String> keys() {
|
||||||
|
return getKeys(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set containing all values in this section.
|
||||||
|
* <p>
|
||||||
|
* If deep is set to true, then this will contain all the keys within any
|
||||||
|
* child {@link ConfigureSection}s (and their children paths).
|
||||||
|
* <p>
|
||||||
|
* If deep is set to false, then this will contain only the keys of any
|
||||||
|
* direct children, and not their own children.
|
||||||
|
*
|
||||||
|
* @param deep Whether to get a deep list.
|
||||||
|
* @return Map of data values contained within this Section.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
Map<String, Object> getValues(boolean deep);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a set containing all key-values in this section.
|
||||||
|
*
|
||||||
|
* @return Map of data values contained within this Section.
|
||||||
|
* @see #getValues(boolean)
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
default Map<String, Object> values() {
|
||||||
|
return getValues(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get this section as a map.
|
||||||
|
* <p>
|
||||||
|
* In this map, child {@link ConfigureSection}s will also be represented as {@link Map}s.
|
||||||
|
*
|
||||||
|
* @return Map of data values contained within this Section.
|
||||||
|
*/
|
||||||
|
@NotNull
|
||||||
|
@UnmodifiableView
|
||||||
|
Map<String, Object> asMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a stream of all values in this section.
|
||||||
|
*
|
||||||
|
* @return Stream of all values in this section.
|
||||||
|
*/
|
||||||
|
default Stream<Map.Entry<String, Object>> stream() {
|
||||||
|
return values().entrySet().stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over all key-values in this section (include child sections)
|
||||||
|
*
|
||||||
|
* @param action The action to apply to each key.
|
||||||
|
*/
|
||||||
|
default void forEach(@NotNull BiConsumer<String, Object> action) {
|
||||||
|
values().forEach(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value at the given path.
|
||||||
|
* <p>
|
||||||
|
* Null values will be kept, if you want to remove a value use {@link #remove(String)}
|
||||||
|
* Path separator depends on holder's
|
||||||
|
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||||
|
*
|
||||||
|
* @param path The path to set the value at.
|
||||||
|
* @param value The value to set.
|
||||||
|
*/
|
||||||
|
void set(@NotNull String path, @Nullable Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the value at the given path.
|
||||||
|
* <p>
|
||||||
|
* Path separator depends on holder's
|
||||||
|
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||||
|
*
|
||||||
|
* @param path The path to remove the value at.
|
||||||
|
*/
|
||||||
|
void remove(@NotNull String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given path is present.
|
||||||
|
* <p>
|
||||||
|
* Path separator depends on holder's
|
||||||
|
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean contains(@NotNull String path) {
|
||||||
|
return getKeys(true).contains(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the value of given path is present.
|
||||||
|
* <p>
|
||||||
|
* Path separator depends on holder's
|
||||||
|
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean containsValue(@NotNull String path) {
|
||||||
|
return get(path) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is specific type.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @param typeClass The type's class
|
||||||
|
* @param <T> The type to check.
|
||||||
|
* @return True if the value is present and is the correct type, false otherwise.
|
||||||
|
*/
|
||||||
|
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
default @Nullable List<?> getList(@NotNull String path) {
|
||||||
|
Object val = get(path);
|
||||||
|
return (val instanceof List<?>) ? (List<?>) val : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Predicate the value of given path is a {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to check.
|
||||||
|
* @return True if the value is present and is a section, false otherwise.
|
||||||
|
*/
|
||||||
|
default boolean isSection(@NotNull String path) {
|
||||||
|
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
|
||||||
|
default ConfigureSection getSection(@NotNull String path) {
|
||||||
|
Object val = get(path);
|
||||||
|
return (val instanceof ConfigureSection) ? (ConfigureSection) val : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ConfigureSection} with specified values.
|
||||||
|
* <p> The {@link #parent()} of the new section will be this section.
|
||||||
|
* <p> This section will not be saved until {@link #set(String, Object)} is called.
|
||||||
|
* <p> If you want to create and use a section and set it to this section, use {@link #computeSection(String)}.
|
||||||
|
*
|
||||||
|
* @param data The data to be used to create section.
|
||||||
|
* @return Newly created section
|
||||||
|
*/
|
||||||
|
@NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ConfigureSection} with specified values.
|
||||||
|
* <p> The {@link #parent()} of the new section will be this section.
|
||||||
|
*
|
||||||
|
* @param data The data to be used to create section.
|
||||||
|
* @return Newly created section
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
|
||||||
|
return createSection(path, () -> {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
data.accept(map);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ConfigureSection} with specified values.
|
||||||
|
* <p> The {@link #parent()} of the new section will be this section.
|
||||||
|
*
|
||||||
|
* @param data The data to be used to create section.
|
||||||
|
* @return Newly created section
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
|
||||||
|
return createSection(path, data.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new empty {@link ConfigureSection}.
|
||||||
|
* <p> The {@link #parent()} of the new section will be this section.
|
||||||
|
*
|
||||||
|
* @return Newly created section
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection createSection(@NotNull String path) {
|
||||||
|
return createSection(path, new LinkedHashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a section at the given path.
|
||||||
|
* <p>
|
||||||
|
* Any value previously set at this path will be replaced if it is not a section
|
||||||
|
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to get the section from.
|
||||||
|
* @return The section at the path, or a new section if it does not exist.
|
||||||
|
* @see #createSection(String, Map)
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection computeSection(@NotNull String path) {
|
||||||
|
return computeSection(path, new LinkedHashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a section at the given path.
|
||||||
|
* <p>
|
||||||
|
* Any value previously set at this path will be replaced if it is not a section
|
||||||
|
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to get the section from.
|
||||||
|
* @return The section at the path, or a new section if it does not exist.
|
||||||
|
* @see #createSection(String, Map)
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||||
|
ConfigureSection current = getSection(path);
|
||||||
|
if (current == null) {
|
||||||
|
current = createSection(path, data);
|
||||||
|
set(path, current);
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a section at the given path.
|
||||||
|
* <p>
|
||||||
|
* Any value previously set at this path will be replaced if it is not a section
|
||||||
|
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to get the section from.
|
||||||
|
* @return The section at the path, or a new section if it does not exist.
|
||||||
|
* @see #createSection(String, Map)
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
|
||||||
|
return computeSection(path, () -> {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
data.accept(map);
|
||||||
|
return map;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or create a section at the given path.
|
||||||
|
* <p>
|
||||||
|
* Any value previously set at this path will be replaced if it is not a section
|
||||||
|
* or will be returned if it is an exists {@link ConfigureSection}.
|
||||||
|
*
|
||||||
|
* @param path The path to get the section from.
|
||||||
|
* @return The section at the path, or a new section if it does not exist.
|
||||||
|
* @see #createSection(String, Map)
|
||||||
|
*/
|
||||||
|
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
|
||||||
|
return computeSection(path, data.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the origin value of the path.
|
||||||
|
*
|
||||||
|
* @param path The path to get the value from.
|
||||||
|
* @return The value at the path, or null if not found.
|
||||||
|
*/
|
||||||
|
@Nullable Object get(@NotNull String path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the path for specific type,
|
||||||
|
* if the path does not exist, return null.
|
||||||
|
*
|
||||||
|
* @param path The path to get the value from.
|
||||||
|
* @param type The type class of the value
|
||||||
|
* @param <T> The type of the value
|
||||||
|
* @return The value at the path, or the default value if not found.
|
||||||
|
*/
|
||||||
|
default @Nullable <T> T get(@NotNull String path, @NotNull Class<T> type) {
|
||||||
|
return get(path, null, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the path using a parser function,
|
||||||
|
* if the path does not exist, return NULL.
|
||||||
|
*
|
||||||
|
* @param path The path to get the value from.
|
||||||
|
* @param parser The function to parse the value
|
||||||
|
* @param <T> The type of the value
|
||||||
|
* @return The value at the path, or null if not found.
|
||||||
|
*/
|
||||||
|
default @Nullable <T> T get(@NotNull String path, @NotNull DataFunction<@Nullable Object, T> parser) {
|
||||||
|
return get(path, null, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 an 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 an 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 an 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
default String childPath(String path) {
|
||||||
|
int index = path.indexOf(pathSeparator());
|
||||||
|
return (index == -1) ? path : path.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T, C extends Collection<T>> @NotNull C parseCollection(
|
||||||
|
@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,135 @@
|
|||||||
|
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.*;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ConfigureSource represents the source of configuration,
|
||||||
|
* which can be a file, a database, or any other source.
|
||||||
|
*
|
||||||
|
* @param <SECTION> The type of the root section.
|
||||||
|
* @param <ORIGINAL> The original configuration object.
|
||||||
|
* @param <SELF> The type of the source itself, for further implement support.
|
||||||
|
* @see ConfigureSection
|
||||||
|
*/
|
||||||
|
public abstract class ConfigureSource<
|
||||||
|
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 String path() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||||
|
return section().getValues(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) {
|
||||||
|
return section().getKeys(deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull @UnmodifiableView Map<String, Object> asMap() {
|
||||||
|
return section().asMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
|
||||||
|
return section().createSection(path, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@NotNull String path, @Nullable Object value) {
|
||||||
|
section().set(path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(@NotNull String path) {
|
||||||
|
section().remove(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, U> extends ValueManifest<T, U> {
|
||||||
|
|
||||||
|
protected ConfigValue(@NotNull ValueManifest<T, U> 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,163 @@
|
|||||||
|
package cc.carm.lib.configuration.value;
|
||||||
|
|
||||||
|
import cc.carm.lib.configuration.adapter.ValueType;
|
||||||
|
import cc.carm.lib.configuration.function.ValueValidator;
|
||||||
|
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.ApiStatus;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class ValueManifest<TYPE, UNIT> {
|
||||||
|
|
||||||
|
protected final @NotNull ValueType<TYPE> type;
|
||||||
|
protected final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer;
|
||||||
|
|
||||||
|
protected @Nullable ConfigurationHolder<?> holder;
|
||||||
|
protected @Nullable String path; // Section path
|
||||||
|
|
||||||
|
protected @NotNull ValueValidator<UNIT> validator;
|
||||||
|
protected @NotNull Supplier<@Nullable TYPE> defaultSupplier;
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull ValueType<TYPE> type) {
|
||||||
|
this(type, () -> null, ValueValidator.none(), EMPTY_INITIALIZER, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull TYPE defaultValue) {
|
||||||
|
this(ValueType.of(defaultValue), () -> defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull ValueType<TYPE> type, @NotNull Supplier<@Nullable TYPE> defaultSupplier) {
|
||||||
|
this(type, defaultSupplier, ValueValidator.none(), EMPTY_INITIALIZER, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull ValueType<TYPE> type,
|
||||||
|
@NotNull Supplier<@Nullable TYPE> defaultSupplier,
|
||||||
|
@NotNull ValueValidator<UNIT> validator) {
|
||||||
|
this(type, defaultSupplier, validator, EMPTY_INITIALIZER, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull ValueType<TYPE> type, @NotNull Supplier<@Nullable TYPE> defaultSupplier,
|
||||||
|
@NotNull ValueValidator<UNIT> validator,
|
||||||
|
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer) {
|
||||||
|
this(type, defaultSupplier, validator, initializer, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueManifest(@NotNull ValueType<TYPE> type,
|
||||||
|
@NotNull Supplier<@Nullable TYPE> defaultSupplier,
|
||||||
|
@NotNull ValueValidator<UNIT> validator,
|
||||||
|
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer,
|
||||||
|
@Nullable ConfigurationHolder<?> holder, @Nullable String path) {
|
||||||
|
this.type = type;
|
||||||
|
this.validator = validator;
|
||||||
|
this.initializer = initializer;
|
||||||
|
this.defaultSupplier = defaultSupplier;
|
||||||
|
this.holder = holder;
|
||||||
|
this.path = path;
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ValueManifest(@NotNull ValueManifest<TYPE, UNIT> manifest) {
|
||||||
|
this(manifest.type, manifest.defaultSupplier, manifest.validator, 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<TYPE> type() {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void holder(@NotNull ConfigurationHolder<?> holder) {
|
||||||
|
this.holder = holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void path(@NotNull String path) {
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable TYPE defaults() {
|
||||||
|
return this.defaultSupplier.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defaults(@Nullable TYPE defaultValue) {
|
||||||
|
defaults(() -> defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void defaults(@NotNull Supplier<@Nullable TYPE> defaultValue) {
|
||||||
|
this.defaultSupplier = defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasDefaults() {
|
||||||
|
return defaults() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ValueValidator<UNIT> validator() {
|
||||||
|
return this.validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validator(@NotNull ValueValidator<UNIT> validator) {
|
||||||
|
this.validator = validator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validate(@NotNull ValueValidator<UNIT> validator) {
|
||||||
|
validator(this.validator.and(validator));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UNIT withValidated(@Nullable UNIT value) throws Exception {
|
||||||
|
validator.validate(holder(), value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull String path() {
|
||||||
|
if (path != null) return path;
|
||||||
|
else throw new IllegalStateException("No section path provided for Value(" + type() + ").");
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfigurationHolder<?> holder() {
|
||||||
|
if (this.holder != null) return this.holder;
|
||||||
|
throw new IllegalStateException("Value(" + type() + ") does not have a provider.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfigureSource<?, ?, ?> config() {
|
||||||
|
return holder().config();
|
||||||
|
}
|
||||||
|
|
||||||
|
public @NotNull ConfigurationMetaHolder metadata() {
|
||||||
|
return holder().metadata(path());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
protected @Nullable Object getData() {
|
||||||
|
return config().get(path());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
protected void setData(@Nullable Object value) {
|
||||||
|
config().set(path(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void throwing(@NotNull Throwable throwable) {
|
||||||
|
throwing(path, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void throwing(@NotNull String path, @NotNull Throwable throwable) {
|
||||||
|
if (holder == null) throwable.printStackTrace();
|
||||||
|
else holder.throwing(path, throwable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, valuePath) -> {
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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, U> extends ConfigValue<T, U> {
|
||||||
|
|
||||||
|
protected @Nullable T cachedValue;
|
||||||
|
protected long parsedTime = -1;
|
||||||
|
|
||||||
|
protected CachedConfigValue(@NotNull ValueManifest<T, U> 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,275 @@
|
|||||||
|
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.builder.list.SourceListBuilder;
|
||||||
|
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>, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> @NotNull SourceListBuilder<T, T> with(@NotNull Class<T> registeredType) {
|
||||||
|
return with(ValueType.of(registeredType));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> @NotNull SourceListBuilder<T, T> with(@NotNull ValueType<T> registeredType) {
|
||||||
|
return new ConfigListBuilder<>(registeredType).from(registeredType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
public static <T> @NotNull ConfiguredList<T> of(@NotNull T value, @NotNull T... values) {
|
||||||
|
List<T> list = new ArrayList<>();
|
||||||
|
list.add(value);
|
||||||
|
Collections.addAll(list, values);
|
||||||
|
return with(ValueType.of(value)).defaults(list).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final @NotNull Supplier<? extends List<V>> constructor;
|
||||||
|
protected final @NotNull ValueAdapter<V> paramAdapter;
|
||||||
|
|
||||||
|
public ConfiguredList(@NotNull ValueManifest<List<V>, 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();
|
||||||
|
try {
|
||||||
|
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);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (Object dataVal : data) {
|
||||||
|
if (dataVal == null) continue;
|
||||||
|
try {
|
||||||
|
list.add(withValidated(parser.parse(holder(), paramType(), dataVal)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throwing(path + "[" + i + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throwing(ex);
|
||||||
|
}
|
||||||
|
return updateCache(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(@Nullable List<V> list) {
|
||||||
|
updateCache(list);
|
||||||
|
if (list == null) {
|
||||||
|
setData(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValueSerializer<V> serializer = serializer();
|
||||||
|
if (serializer == null) return;
|
||||||
|
|
||||||
|
List<Object> data = new ArrayList<>();
|
||||||
|
for (V val : list) {
|
||||||
|
if (val == null) continue;
|
||||||
|
try {
|
||||||
|
data.add(serializer.serialize(holder(), paramType(), withValidated(val)));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throwing(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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,225 @@
|
|||||||
|
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>, V> implements Map<K, V> {
|
||||||
|
|
||||||
|
public static <V> ConfigMapCreator<String, V> builderOf(@NotNull Class<V> valueType) {
|
||||||
|
return builderOf(String.class, valueType);
|
||||||
|
}
|
||||||
|
|
||||||
|
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>, 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();
|
||||||
|
|
||||||
|
try {
|
||||||
|
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);
|
||||||
|
if (keyParser == null) return getDefaultFirst(map);
|
||||||
|
ValueParser<V> valueParser = parserFor(valueAdapter);
|
||||||
|
if (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, withValidated(value));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throwing(path + "." + dataKey, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throwing(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ValueSerializer<K> keySerializer = serializerFor(keyAdapter);
|
||||||
|
if (keySerializer == null) return;
|
||||||
|
ValueSerializer<V> valueSerializer = serializerFor(valueAdapter);
|
||||||
|
if (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(), withValidated(entry.getValue()))
|
||||||
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throwing(path + "." + entry.getKey(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setData(data);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throwing(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,184 @@
|
|||||||
|
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.builder.value.SourceValueBuilder;
|
||||||
|
import cc.carm.lib.configuration.source.ConfigurationHolder;
|
||||||
|
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, V> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new value builder.
|
||||||
|
*
|
||||||
|
* @param type The type of the value.
|
||||||
|
* @param <V> The type of the value.
|
||||||
|
* @return a {@link ConfigValueBuilder} with the specified type.
|
||||||
|
*/
|
||||||
|
public static <V> ConfigValueBuilder<V> builderOf(@NotNull Class<V> type) {
|
||||||
|
return new ConfigValueBuilder<>(ValueType.of(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new value builder.
|
||||||
|
*
|
||||||
|
* @param type The type of the value.
|
||||||
|
* @param <V> The type of the value.
|
||||||
|
* @return a {@link ConfigValueBuilder} with the specified type.
|
||||||
|
*/
|
||||||
|
public static <V> ConfigValueBuilder<V> builderOf(@NotNull ValueType<V> type) {
|
||||||
|
return new ConfigValueBuilder<>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new value builder with the specified {@link ConfigurationHolder#registeredValues()} type.
|
||||||
|
*
|
||||||
|
* @param registeredType The type of the value.
|
||||||
|
* @param <V> The type of the value.
|
||||||
|
* @return a {@link SourceValueBuilder} with the specified registered type.
|
||||||
|
* @see ValueAdapter
|
||||||
|
*/
|
||||||
|
public static <V> SourceValueBuilder<V, V> with(@NotNull Class<V> registeredType) {
|
||||||
|
return with(ValueType.of(registeredType));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new value builder with the specified {@link ConfigurationHolder#registeredValues()} type.
|
||||||
|
*
|
||||||
|
* @param registeredType The type of the value.
|
||||||
|
* @param <V> The type of the value.
|
||||||
|
* @return a {@link SourceValueBuilder} with the specified registered type.
|
||||||
|
* @see ValueAdapter
|
||||||
|
*/
|
||||||
|
public static <V> SourceValueBuilder<V, V> with(@NotNull ValueType<V> registeredType) {
|
||||||
|
return new ConfigValueBuilder<>(registeredType).from(registeredType);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 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, V> manifest, @NotNull ValueAdapter<V> adapter) {
|
||||||
|
return new ConfiguredValue<>(manifest, adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final @NotNull ValueAdapter<V> adapter;
|
||||||
|
|
||||||
|
public ConfiguredValue(@NotNull ValueManifest<V, 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.
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object data = getData();
|
||||||
|
if (data == null) return defaults();
|
||||||
|
|
||||||
|
ValueParser<V> parser = parser();
|
||||||
|
if (parser == null) return defaults(); // No parser, return default value.
|
||||||
|
|
||||||
|
// If there are no errors, update the cache and return.
|
||||||
|
V parsed = parser.parse(holder(), type(), data);
|
||||||
|
return updateCache(withValidated(parsed));
|
||||||
|
} catch (Exception e) {
|
||||||
|
// There was a validate or parsing error, prompted and returned the default value.
|
||||||
|
throwing(e);
|
||||||
|
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(@Nullable V value) {
|
||||||
|
updateCache(value); // Update cache
|
||||||
|
if (value == null) {
|
||||||
|
setData(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ValueSerializer<V> serializer = serializer();
|
||||||
|
if (serializer == null) return; // No serializer, do nothing.
|
||||||
|
|
||||||
|
setData(serializer.serialize(holder(), type(), withValidated(value)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throwing(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
import cc.carm.lib.configuration.source.loader.PathGenerator;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class NameTest {
|
public class NameTest {
|
||||||
@@ -7,10 +7,10 @@ public class NameTest {
|
|||||||
@Test
|
@Test
|
||||||
public void onTest() {
|
public void onTest() {
|
||||||
|
|
||||||
System.out.println(ConfigInitializer.getPathFromName("LoveGames")); // -> love-games
|
System.out.println(PathGenerator.covertPathName("LoveGames")); // -> love-games
|
||||||
System.out.println(ConfigInitializer.getPathFromName("EASY_GAME")); // -> easy-game
|
System.out.println(PathGenerator.covertPathName("EASY_GAME")); // -> easy-game
|
||||||
System.out.println(ConfigInitializer.getPathFromName("F")); //-? f
|
System.out.println(PathGenerator.covertPathName("F")); //-? f
|
||||||
System.out.println(ConfigInitializer.getPathFromName("Test123123")); // -? test123123123
|
System.out.println(PathGenerator.covertPathName("Test123123")); // -? test123123123
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+26
-4
@@ -3,9 +3,9 @@
|
|||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>easyconfiguration-parent</artifactId>
|
<artifactId>configured-parent</artifactId>
|
||||||
<groupId>cc.carm.lib</groupId>
|
<groupId>cc.carm.lib</groupId>
|
||||||
<version>3.9.0</version>
|
<version>4.1.2</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<properties>
|
<properties>
|
||||||
@@ -13,15 +13,37 @@
|
|||||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||||
|
|
||||||
</properties>
|
</properties>
|
||||||
<artifactId>easyconfiguration-demo</artifactId>
|
<artifactId>configured-demo</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.parent.groupId}</groupId>
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
<artifactId>easyconfiguration-core</artifactId>
|
<artifactId>configured-core</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>configured-feature-commentable</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>configured-feature-versioned</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.parent.groupId}</groupId>
|
||||||
|
<artifactId>configured-feature-validators</artifactId>
|
||||||
<version>${project.parent.version}</version>
|
<version>${project.parent.version}</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user