mirror of
https://github.com/CarmJos/EasyConfiguration.git
synced 2026-06-04 18:48:20 +08:00
Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 505b86ec9c | |||
| 374f646f9e | |||
| aa50345329 | |||
| db8b227317 | |||
| 60eed8a14d | |||
| d41630be72 | |||
| 1c3a2b01ea | |||
| 1ede74c692 | |||
| 67456a8aac | |||
| f422900fb5 | |||
| b3e0007eba | |||
| 89c8ec8adf | |||
| e8f49bf8bc | |||
| 8ac980fdbb | |||
| 9f2e4bc0cb | |||
| ae16d131c3 | |||
| bd8470a6e8 | |||
| 6af2d3fef5 | |||
| 00f83002c5 | |||
| 0aa548cbbf | |||
| 9c0ed1c5c2 | |||
| bb0998cbac | |||
| 1eb16f00b6 | |||
| 5ccf63b423 | |||
| f1c0c74574 | |||
| 8ee074474c | |||
| 27a68ead7c | |||
| c6cce5208f | |||
| bc67de06f6 | |||
| b668794f5d | |||
| 07424284b7 | |||
| 81e024e309 | |||
| 763fc7c758 | |||
| 56557221a4 | |||
| e4435bf883 | |||
| eee4a278d9 | |||
| 3a0a8e79b9 | |||
| 3b2b1b27cc | |||
| d84ea1b7da | |||
| a1f2cdca04 | |||
| c52183aadd | |||
| d71aabad2d | |||
| 6a007c5187 | |||
| 43b00f2b69 | |||
| 2e61e66cdb | |||
| 39f946c28e | |||
| 25931ffd7e | |||
| de103da879 | |||
| 457c22d461 | |||
| aa4225dbba | |||
| ddd33154be | |||
| 727c26a2fb | |||
| 9c95a16d90 | |||
| 92c05f1a59 | |||
| 739ed41885 | |||
| a66da01996 | |||
| 6dc0447502 | |||
| c49d904665 | |||
| b756074ddc | |||
| 9e3dff3e95 | |||
| fd01d9b7ef | |||
| 0f8383bbf3 | |||
| 1232c7c4da | |||
| 7ac39da4e9 | |||
| bf89f583db | |||
| 03c69ba3a2 | |||
| d9cbd1a283 | |||
| 96e90dd71b | |||
| a9f3d829bd | |||
| 8eefba5159 | |||
| 01e20df559 | |||
| 35398ab741 | |||
| 6f97166192 | |||
| ccd239ad6b | |||
| 69b27476bc | |||
| 0651cac6b0 | |||
| 1a1efad283 | |||
| 3c1ba61b61 | |||
| 1a3e84a787 | |||
| 00228db2c4 | |||
| 0fddfe28af | |||
| 4a17089da0 | |||
| 8faa7b1c24 | |||
| 5e525428fe | |||
| bc0dfd5698 | |||
| dc28d743db | |||
| f61294c5f3 | |||
| 6883a464db | |||
| bcdf0d9bd1 | |||
| 5f89ff4db7 | |||
| d6f4970277 | |||
| c045ca1489 | |||
| ceea900b08 | |||
| 85bacb24f3 | |||
| 6b3a353fcc | |||
| 2c026fc0b0 | |||
| f8b4bbd3a9 | |||
| e9a0f0ff30 | |||
| e3fe6e7c80 | |||
| c179fa2ccd | |||
| 390815b790 | |||
| 760ac815df | |||
| 216050a701 | |||
| 6d0ee35197 | |||
| 494491cf94 | |||
| 00e88b50ff | |||
| 033236c89b | |||
| 791fa6e5b4 | |||
| 51c287a0a7 | |||
| ab2f898164 | |||
| 4f4b203240 | |||
| c94fef893f | |||
| c2a08c6c72 | |||
| dd7a6c819f | |||
| e9c010981e | |||
| 78d52e1aae | |||
| 42ccc23347 | |||
| 85578e3d8c | |||
| dc4d3664d3 | |||
| 2e4cb5480a | |||
| 4b7a7aeae7 | |||
| 132ca81635 | |||
| 80747ac922 | |||
| 0d10a06547 | |||
| 0bda97d82a | |||
| a13ea7569c | |||
| c9f488c932 |
@@ -0,0 +1,16 @@
|
||||
# 欢迎使用 EasyConfiguration !
|
||||
|
||||
这个项目刚刚创建,详细的Javadoc与开发指南还在补充,请给我一点时间~
|
||||
|
||||
|
||||
## 基本定义
|
||||
|
||||
Value: 实际配置的单例值。
|
||||
|
||||
Manifest: 用于描述值基本配置的对象。
|
||||
|
||||
Provider: 用于提供配置文件的接口。
|
||||
|
||||
Wrapper: 用于包装配置文件的接口。
|
||||
|
||||
Initializer: 用于初始化的接口
|
||||
@@ -1,7 +1,7 @@
|
||||
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: "Project Deploy"
|
||||
name: "Deploy & Publish"
|
||||
|
||||
on:
|
||||
# 支持手动触发构建
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
rm -rf docs
|
||||
mkdir -vp docs
|
||||
cp -vrf core/target/apidocs/* docs/
|
||||
cp -vrf .documentation/JAVADOC-README.md docs/README.md
|
||||
cp -vrf .doc/JAVADOC-README.md docs/README.md
|
||||
|
||||
- name: "Generate the Javadoc sitemap"
|
||||
id: sitemap
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||
|
||||
name: Project Build & Tests
|
||||
name: Build & Tests
|
||||
|
||||
on:
|
||||
# 支持手动触发构建
|
||||
|
||||
@@ -1,21 +1,165 @@
|
||||
MIT License
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (c) 2022 Carm
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
@@ -1,67 +1,64 @@
|
||||
```text
|
||||
____ _____ ____ __ _
|
||||
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
|
||||
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
|
||||
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
|
||||
/___/ /___/
|
||||
```
|
||||
|
||||
README LANGUAGES [ [**English**](README.md) | [中文](README_CN.md) ]
|
||||
|
||||
# EasyConfiguration
|
||||
|
||||
[](https://github.com/CarmJos/EasyConfiguration/releases)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](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!**
|
||||
|
||||
## 优势
|
||||
A simple, easy-to-use and universal solution for managing configuration files.
|
||||
Enjoy the ease of use with customizable formats for loading, reading, and updating your configuration files.
|
||||
|
||||
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
|
||||
## Features & Advantages
|
||||
|
||||
## 开发
|
||||
- Class-based mechanism for initializing, loading, retrieving, and updating configuration files, ensuring convenience and efficiency.
|
||||
- Supports manual serialization and deserialization of complex configurations.
|
||||
- Offers multiple builder forms for rapid construction of `ConfigValue<?>` objects.
|
||||
- Enables specification of configuration paths, comments, and more via annotations.
|
||||
|
||||
详细开发介绍请 [点击这里](.documentation/README.md) , JavaDoc(最新Release) 请 [点击这里](https://carmjos.github.io/EasyConfiguration) 。
|
||||
## Development
|
||||
|
||||
### 示例代码
|
||||
For a detailed development guide, [CLICK HERE](.doc/README.md).
|
||||
|
||||
您可以 [点击这里](impl/yaml/src/test/java/config/source/TestConfiguration.java) 查看部分代码演示,更多演示详见 [开发介绍](.documentation/README.md) 。
|
||||
For the latest JavaDoc release, [CLICK HERE](https://CarmJos.github.io/EasyConfiguration).
|
||||
|
||||
### 依赖方式
|
||||
### Code Samples
|
||||
|
||||
#### Maven 依赖
|
||||
Check out some code demonstrations [HERE](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java).
|
||||
|
||||
For more examples, see the [Development Guide](.doc/README.md).
|
||||
|
||||
### Dependencies
|
||||
|
||||
#### Maven Dependency
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
<summary>Remote Repository Configuration</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!--采用Maven中心库,安全稳定,但版本更新需要等待同步-->
|
||||
<!-- Using Maven Central Repository for secure and stable updates, though synchronization might be needed. -->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用github依赖库,实时更新,但需要配置 (推荐) -->
|
||||
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
|
||||
<id>EasyConfiguration</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用我的私人依赖库,简单方便,但可能因为变故而无法使用-->
|
||||
<id>carm-repo</id>
|
||||
<name>Carm's Repo</name>
|
||||
<url>https://repo.carm.cc/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
</project>
|
||||
```
|
||||
@@ -69,13 +66,12 @@
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
<summary>Generic Native Dependency</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<!--基础实现部分,需要自行实现“Provider”与“Wrapper”。-->
|
||||
<!-- Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”. -->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
@@ -83,7 +79,7 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于YAML文件的实现版本,可用于全部Java环境。-->
|
||||
<!-- YAML file-based implementation, compatible with all Java environments. -->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-yaml</artifactId>
|
||||
@@ -91,131 +87,80 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments. -->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-json</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**基于Spigot实现的版本**
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-spigot</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
**基于Bungee实现的版本 (不支持自动注释)**
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-bungee</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Gradle 依赖
|
||||
#### Gradle Dependency
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
<summary>Remote Repository Configuration</summary>
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// 采用Maven中心库,安全稳定,但版本更新需要等待同步
|
||||
// Using Maven Central Repository for secure and stable updates, though synchronization might be needed.
|
||||
mavenCentral()
|
||||
|
||||
// 采用github依赖库,实时更新,但需要配置 (推荐)
|
||||
// Using GitHub dependencies for real-time updates, configuration required (recommended).
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
||||
|
||||
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
|
||||
maven { url 'https://repo.carm.cc/repository/maven-public/' }
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
<summary>Generic Native Dependency</summary>
|
||||
|
||||
```groovy
|
||||
|
||||
dependencies {
|
||||
|
||||
//基础实现部分,需要自行实现“Provider”与“Wrapper”。
|
||||
// Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”.
|
||||
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
|
||||
|
||||
//基于YAML文件的实现版本,可用于全部Java环境。
|
||||
// YAML file-based implementation, compatible with all Java environments.
|
||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>平台依赖版本</summary>
|
||||
|
||||
```groovy
|
||||
|
||||
dependencies {
|
||||
|
||||
// 用于Spigot服务端的版本
|
||||
api "cc.carm.lib:easyconfiguration-spigot:[LATEST RELEASE]"
|
||||
|
||||
// 用于BungeeCord服务端的版本,不支持注解。
|
||||
// 如需注解,可选择使用 `easyconfiguration-yaml` 并打包。
|
||||
api "cc.carm.lib:easyconfiguration-bungee:[LATEST RELEASE]"
|
||||
// JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments.
|
||||
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 支持与捐赠
|
||||
## Derived Projects
|
||||
|
||||
若您觉得本插件做的不错,您可以通过捐赠支持我!
|
||||
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos)
|
||||
|
||||
感谢您对开源项目的支持!
|
||||
EasyConfiguration for MineCraft!
|
||||
Easily manage configurations on MineCraft-related server platforms.
|
||||
|
||||
<img height=25% width=25% src="https://raw.githubusercontent.com/CarmJos/CarmJos/main/img/donate-code.jpg" alt=""/>
|
||||
Currently supports BungeeCord, Bukkit (Spigot) servers, with more platforms to be supported soon.
|
||||
|
||||
## 开源协议
|
||||
## Support and Donation
|
||||
|
||||
本项目源码采用 [The MIT License](https://opensource.org/licenses/MIT) 开源协议。
|
||||
If you appreciate this plugin, consider supporting me with a donation!
|
||||
|
||||
<details>
|
||||
<summary>关于 MIT 协议</summary>
|
||||
Thank you for supporting open-source projects!
|
||||
|
||||
> MIT 协议可能是几大开源协议中最宽松的一个,核心条款是:
|
||||
>
|
||||
> 该软件及其相关文档对所有人免费,可以任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。唯一的限制是,软件中必须包含上述版 权和许可提示。
|
||||
>
|
||||
> 这意味着:
|
||||
>
|
||||
> - 你可以自由使用,复制,修改,可以用于自己的项目。
|
||||
> - 可以免费分发或用来盈利。
|
||||
> - 唯一的限制是必须包含许可声明。
|
||||
>
|
||||
> MIT 协议是所有开源许可中最宽松的一个,除了必须包含许可声明外,再无任何限制。
|
||||
>
|
||||
> *以上文字来自 [五种开源协议GPL,LGPL,BSD,MIT,Apache](https://www.oschina.net/question/54100_9455) 。*
|
||||
Many thanks to Jetbrains for kindly providing a license for us to work on this and other open-source projects.
|
||||
|
||||
</details>
|
||||
[](https://www.jetbrains.com/?from=https://github.com/ArtformGames/ResidenceList)
|
||||
|
||||
|
||||
## 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).
|
||||
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
```text
|
||||
____ _____ ____ __ _
|
||||
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
|
||||
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
|
||||
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
|
||||
/___/ /___/
|
||||
```
|
||||
|
||||
README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
|
||||
|
||||
# EasyConfiguration
|
||||
|
||||
[](https://github.com/CarmJos/EasyConfiguration/releases)
|
||||
[](https://www.gnu.org/licenses/lgpl-3.0.html)
|
||||
[](https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml)
|
||||
[](https://www.codefactor.io/repository/github/carmjos/easyconfiguration)
|
||||

|
||||

|
||||
|
||||
**轻松(做)配置!**
|
||||
|
||||
一款简单便捷的通用配置文件加载、读取与更新工具,可自定义配置的格式。
|
||||
|
||||
## 特性 & 优势
|
||||
|
||||
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
|
||||
- 支持复杂配置的手动序列化、反序列化。
|
||||
- 提供多种builder形式,快速构建 `ConfigValue<?>` 对象。
|
||||
- 支持通过注解规定配置对应的路径、注释等信息。
|
||||
|
||||
## 开发
|
||||
|
||||
详细开发介绍请 [点击这里](.doc/README.md) , JavaDoc(最新Release) 请 [点击这里](https://CarmJos.github.io/EasyConfiguration) 。
|
||||
|
||||
### 示例代码
|
||||
|
||||
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java) 查看部分代码演示,更多演示详见 [开发介绍](.doc/README.md) 。
|
||||
|
||||
### 依赖方式
|
||||
|
||||
#### Maven 依赖
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<repositories>
|
||||
|
||||
<repository>
|
||||
<!--采用Maven中心库,安全稳定,但版本更新需要等待同步-->
|
||||
<id>maven</id>
|
||||
<name>Maven Central</name>
|
||||
<url>https://repo1.maven.org/maven2</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用github依赖库,实时更新,但需要配置 (推荐) -->
|
||||
<id>EasyConfiguration</id>
|
||||
<name>GitHub Packages</name>
|
||||
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
|
||||
</repository>
|
||||
|
||||
<repository>
|
||||
<!--采用我的私人依赖库,简单方便,但可能因为变故而无法使用-->
|
||||
<id>carm-repo</id>
|
||||
<name>Carm's Repo</name>
|
||||
<url>https://repo.carm.cc/repository/maven-public/</url>
|
||||
</repository>
|
||||
|
||||
</repositories>
|
||||
</project>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
|
||||
```xml
|
||||
|
||||
<project>
|
||||
<dependencies>
|
||||
<!--基础实现部分,需要自行实现“Provider”与“Wrapper”。-->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于YAML文件的实现版本,可用于全部Java环境。-->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-yaml</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<!--基于JSON文件的实现版本,可用于全部Java环境。-->
|
||||
<!--需要注意的是,JSON不支持文件注释。-->
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easyconfiguration-json</artifactId>
|
||||
<version>[LATEST RELEASE]</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
#### Gradle 依赖
|
||||
|
||||
<details>
|
||||
<summary>远程库配置</summary>
|
||||
|
||||
```groovy
|
||||
repositories {
|
||||
|
||||
// 采用Maven中心库,安全稳定,但版本更新需要等待同步
|
||||
mavenCentral()
|
||||
|
||||
// 采用github依赖库,实时更新,但需要配置 (推荐)
|
||||
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
|
||||
|
||||
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
|
||||
maven { url 'https://repo.carm.cc/repository/maven-public/' }
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>通用原生依赖</summary>
|
||||
|
||||
```groovy
|
||||
|
||||
dependencies {
|
||||
|
||||
//基础实现部分,需要自行实现“Provider”与“Wrapper”。
|
||||
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
|
||||
|
||||
//基于YAML文件的实现版本,可用于全部Java环境。
|
||||
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
|
||||
|
||||
//基于JSON文件的实现版本,可用于全部Java环境。
|
||||
//需要注意的是,JSON不支持文件注释。
|
||||
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 衍生项目
|
||||
|
||||
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos )
|
||||
|
||||
EasyConfiguration for MineCraft!
|
||||
开始在 MineCraft 相关服务器平台上轻松(做)配置吧!
|
||||
|
||||
目前支持 BungeeCord, Bukkit(Spigot) 服务端,后续将支持更多平台。
|
||||
|
||||
## 支持与捐赠
|
||||
|
||||
若您觉得本插件做的不错,您可以通过捐赠支持我!
|
||||
|
||||
感谢您对开源项目的支持!
|
||||
|
||||
万分感谢 Jetbrains 为我们提供了从事此项目和其他开源项目的许可!
|
||||
|
||||
[](https://www.jetbrains.com/?from=https://github.com/ArtformGames/ResidenceList)
|
||||
|
||||
## 开源协议
|
||||
|
||||
本项目源码采用 [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html) 开源协议。
|
||||
+5
-3
@@ -5,12 +5,14 @@
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>1.0.6</version>
|
||||
<version>3.9.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
|
||||
@@ -1,106 +1,257 @@
|
||||
package cc.carm.lib.configuration.core;
|
||||
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigComment;
|
||||
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.lang.reflect.Modifier;
|
||||
import java.util.Optional;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ConfigInitializer {
|
||||
/**
|
||||
* 配置文件类初始化方法
|
||||
* 用于初始化 {@link Configuration} 中的每个 {@link ConfigValue} 对象
|
||||
*
|
||||
* @param <T> {@link ConfigurationProvider} 配置文件的数据来源
|
||||
* @author CarmJos
|
||||
*/
|
||||
public class ConfigInitializer<T extends ConfigurationProvider<?>> {
|
||||
|
||||
public static void initialize(ConfigurationProvider source, Class<? extends ConfigurationRoot> rootClazz) {
|
||||
initialize(source, rootClazz, true);
|
||||
protected final @NotNull T provider;
|
||||
|
||||
public ConfigInitializer(@NotNull T provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public static void initialize(ConfigurationProvider provider, Class<? extends ConfigurationRoot> rootClazz, boolean saveDefault) {
|
||||
String rootSection = null;
|
||||
|
||||
ConfigPath sectionAnnotation = rootClazz.getAnnotation(ConfigPath.class);
|
||||
if (sectionAnnotation != null && sectionAnnotation.value().length() > 1) {
|
||||
rootSection = sectionAnnotation.value();
|
||||
/**
|
||||
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
|
||||
*
|
||||
* @param clazz 配置文件类,须继承于 {@link Configuration} 。
|
||||
* @param saveDefaults 是否写入默认值(默认为 true)。
|
||||
*/
|
||||
public void initialize(@NotNull Class<? extends Configuration> clazz, boolean saveDefaults) {
|
||||
initialize(clazz, saveDefaults, true);
|
||||
}
|
||||
|
||||
if (rootSection != null) {
|
||||
//Not usable for null section.
|
||||
ConfigComment comments = rootClazz.getAnnotation(ConfigComment.class);
|
||||
if (comments != null && comments.value().length > 0) {
|
||||
provider.setComments(rootSection, comments.value());
|
||||
}
|
||||
}
|
||||
|
||||
for (Class<?> innerClass : rootClazz.getDeclaredClasses()) {
|
||||
initSection(provider, rootSection, innerClass, saveDefault);
|
||||
}
|
||||
|
||||
for (Field field : rootClazz.getDeclaredFields()) {
|
||||
initValue(provider, rootSection, rootClazz, field, saveDefault);
|
||||
}
|
||||
|
||||
if (saveDefault) {
|
||||
/**
|
||||
* 初始化指定类的所有 {@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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void initSection(ConfigurationProvider provider, String parentSection, Class<?> clazz, boolean saveDefault) {
|
||||
if (!Modifier.isStatic(clazz.getModifiers()) || !Modifier.isPublic(clazz.getModifiers())) return;
|
||||
|
||||
String section = getSectionPath(clazz.getSimpleName(), parentSection, clazz.getAnnotation(ConfigPath.class));
|
||||
ConfigComment comments = clazz.getAnnotation(ConfigComment.class);
|
||||
if (comments != null && comments.value().length > 0) {
|
||||
provider.setComments(parentSection, comments.value());
|
||||
/**
|
||||
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
|
||||
*
|
||||
* @param config 配置文件实例类,须实现 {@link Configuration} 。
|
||||
*/
|
||||
public void initialize(@NotNull Configuration config) {
|
||||
initialize(config, true);
|
||||
}
|
||||
|
||||
for (Field field : clazz.getDeclaredFields()) initValue(provider, section, clazz, field, saveDefault);
|
||||
for (Class<?> innerClass : clazz.getDeclaredClasses()) initSection(provider, section, innerClass, saveDefault);
|
||||
|
||||
}
|
||||
|
||||
private static void initValue(ConfigurationProvider provider, String parentSection, Class<?> clazz, Field field, boolean saveDefault) {
|
||||
/**
|
||||
* 初始化指定实例的所有 {@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 {
|
||||
field.setAccessible(true);
|
||||
Object object = field.get(clazz);
|
||||
if (object instanceof ConfigValue<?>) {
|
||||
initializeValue(
|
||||
provider, (ConfigValue<?>) object, saveDefault,
|
||||
getSectionPath(field.getName(), parentSection, field.getAnnotation(ConfigPath.class)),
|
||||
Optional.ofNullable(field.getAnnotation(ConfigComment.class))
|
||||
.map(ConfigComment::value).orElse(new String[0])
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
public static void initializeValue(@NotNull ConfigurationProvider provider, @NotNull ConfigValue<?> value,
|
||||
boolean saveDefault, @NotNull String path, @NotNull String[] comments) {
|
||||
value.initialize(provider, path, comments);
|
||||
if (saveDefault && value.getDefaultValue() != null && !provider.getConfiguration().contains(path)) {
|
||||
value.setDefault();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String getSectionPath(@NotNull String name,
|
||||
@Nullable String parentSection,
|
||||
@Nullable ConfigPath pathAnnotation) {
|
||||
@NotNull String parent = parentSection != null ? parentSection + "." : "";
|
||||
@NotNull String path = getSectionName(name);
|
||||
boolean root = false;
|
||||
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) {
|
||||
if (pathAnnotation.value().length() > 0) path = pathAnnotation.value();
|
||||
root = pathAnnotation.root();
|
||||
fromRoot = pathAnnotation.root();
|
||||
if (pathAnnotation.value().length() > 0) {
|
||||
return (fromRoot ? "" : parent) + pathAnnotation.value();
|
||||
}
|
||||
return (root ? "" : parent) + path;
|
||||
}
|
||||
|
||||
// 最后再通过 fieldName 自动生成路径
|
||||
return (fromRoot ? "" : parent) + getPathFromName(field.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,7 +261,7 @@ public class ConfigInitializer {
|
||||
* @param name 源名称
|
||||
* @return 全小写,以“-”链接 的 路径名称
|
||||
*/
|
||||
public static String getSectionName(String name) {
|
||||
public static String getPathFromName(String name) {
|
||||
return name.replaceAll("[A-Z]", "-$0") // 将驼峰转换为蛇形;
|
||||
.replaceAll("-(.*)", "$1") // 若首字母也为大写,则也会被转换,需要去掉第一个横线
|
||||
.replaceAll("_-([A-Z])", "_$1") // 因为命名中可能包含 _,因此需要被特殊处理一下
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
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,4 +1,8 @@
|
||||
package cc.carm.lib.configuration.core;
|
||||
|
||||
public abstract class ConfigurationRoot {
|
||||
/**
|
||||
* 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,17 +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;
|
||||
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ConfigComment {
|
||||
|
||||
@NotNull
|
||||
String[] value() default "";
|
||||
|
||||
}
|
||||
@@ -16,7 +16,7 @@ public @interface ConfigPath {
|
||||
|
||||
/**
|
||||
* 指定路径的值。
|
||||
* 若不指定,则会通过 {@link ConfigInitializer#getSectionName(String)}自动生成当前路径的值。
|
||||
* 若不指定,则会通过 {@link ConfigInitializer#getPathFromName(String)} 自动生成当前路径的值。
|
||||
*
|
||||
* @return 路径的值
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 顶部注释,用于给对应配置的顶部添加注释,便于使用者阅读查看。
|
||||
* <p>如:
|
||||
* <blockquote><pre>
|
||||
* # 注释第一行
|
||||
* # 注释第二行
|
||||
* foo: "bar"
|
||||
* </pre></blockquote>
|
||||
*/
|
||||
@Target({ElementType.TYPE, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface HeaderComment {
|
||||
|
||||
/**
|
||||
* 注释内容,若内容长度为0则会视为一个空行。
|
||||
* <p> 如 <b>{"foo","","bar"}</b>
|
||||
* 会被添加为
|
||||
* <blockquote><pre>
|
||||
* # foo
|
||||
*
|
||||
* # bar
|
||||
* foo: "bar"
|
||||
* </pre></blockquote>
|
||||
*
|
||||
* @return 注释内容
|
||||
*/
|
||||
@NotNull
|
||||
String[] value() default "";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
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 "";
|
||||
|
||||
}
|
||||
+40
-5
@@ -1,24 +1,36 @@
|
||||
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;
|
||||
|
||||
public abstract class AbstractConfigBuilder<B extends AbstractConfigBuilder<B, T>, T> {
|
||||
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 @Nullable ConfigurationProvider provider;
|
||||
protected final Class<? super P> providerClass;
|
||||
|
||||
protected @Nullable P provider;
|
||||
protected @Nullable String path;
|
||||
|
||||
protected @NotNull String[] comments = new String[0];
|
||||
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 ConfigurationProvider provider) {
|
||||
public @NotNull B from(@Nullable P provider) {
|
||||
this.provider = provider;
|
||||
return getThis();
|
||||
}
|
||||
@@ -29,7 +41,20 @@ public abstract class AbstractConfigBuilder<B extends AbstractConfigBuilder<B, T
|
||||
}
|
||||
|
||||
public @NotNull B comments(@NotNull String... comments) {
|
||||
this.comments = 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();
|
||||
}
|
||||
|
||||
@@ -38,5 +63,15 @@ public abstract class AbstractConfigBuilder<B extends AbstractConfigBuilder<B, T
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ 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;
|
||||
|
||||
@@ -19,24 +20,24 @@ public class ConfigBuilder {
|
||||
return new ConfigListBuilder<>(valueClass);
|
||||
}
|
||||
|
||||
public <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asMap(@NotNull Class<K> keyClass,
|
||||
public <K, V> @NotNull ConfigMapCreator<K, V> asMap(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return new ConfigMapBuilder<>(LinkedHashMap::new, keyClass, 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).supplier(HashMap::new);
|
||||
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);
|
||||
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).supplier(TreeMap::new);
|
||||
return asMap(keyClass, valueClass).asTreeMap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+14
-2
@@ -3,6 +3,9 @@ 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;
|
||||
@@ -11,7 +14,7 @@ public class ConfigListBuilder<V> {
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
public @NotNull <S> SourceListBuilder<S, V> from(@NotNull Class<S> sourceClass,
|
||||
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,
|
||||
@@ -31,7 +34,7 @@ public class ConfigListBuilder<V> {
|
||||
return from(
|
||||
Object.class, ConfigDataFunction.identity(),
|
||||
ConfigDataFunction.castObject(valueClass),
|
||||
ConfigDataFunction.identity(), ConfigDataFunction.toObject()
|
||||
ConfigDataFunction.toObject(), ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,4 +46,13 @@ public class ConfigListBuilder<V> {
|
||||
);
|
||||
}
|
||||
|
||||
public @NotNull SourceListBuilder<Map<String, Object>, V> fromMap() {
|
||||
return from(
|
||||
Map.class, obj -> (Map<String, Object>) obj,
|
||||
ConfigDataFunction.required(),
|
||||
ConfigDataFunction.required(),
|
||||
ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+17
-7
@@ -1,16 +1,18 @@
|
||||
package cc.carm.lib.configuration.core.builder.list;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
|
||||
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 AbstractConfigBuilder<SourceListBuilder<S, V>, List<V>> {
|
||||
public class SourceListBuilder<S, V> extends CommonConfigBuilder<List<V>, SourceListBuilder<S, V>> {
|
||||
|
||||
protected final @NotNull Class<S> sourceClass;
|
||||
protected final @NotNull Class<? super S> sourceClass;
|
||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
@@ -19,7 +21,7 @@ public class SourceListBuilder<S, V>
|
||||
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
|
||||
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
|
||||
|
||||
public SourceListBuilder(@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
|
||||
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) {
|
||||
@@ -31,6 +33,15 @@ public class SourceListBuilder<S, V>
|
||||
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;
|
||||
@@ -59,8 +70,7 @@ public class SourceListBuilder<S, V>
|
||||
@Override
|
||||
public @NotNull ConfiguredList<V> build() {
|
||||
return new ConfiguredList<>(
|
||||
this.provider, this.path, this.comments,
|
||||
this.valueClass, this.defaultValue,
|
||||
buildManifest(), this.valueClass,
|
||||
this.sourceParser.andThen(this.valueParser),
|
||||
this.valueSerializer.andThen(sourceSerializer)
|
||||
);
|
||||
|
||||
@@ -48,6 +48,14 @@ public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
+12
-6
@@ -1,15 +1,16 @@
|
||||
package cc.carm.lib.configuration.core.builder.map;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
|
||||
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 AbstractConfigBuilder<SourceMapBuilder<M, S, K, V>, M> {
|
||||
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;
|
||||
|
||||
@@ -52,6 +53,12 @@ public class SourceMapBuilder<M extends Map<K, V>, S, K, V>
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -90,9 +97,8 @@ public class SourceMapBuilder<M extends Map<K, V>, S, K, V>
|
||||
@Override
|
||||
public @NotNull ConfiguredMap<K, V> build() {
|
||||
return new ConfiguredMap<>(
|
||||
this.provider, this.path, this.comments,
|
||||
this.defaultValue, this.supplier,
|
||||
this.keyClass, this.keyParser,
|
||||
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)
|
||||
);
|
||||
|
||||
+5
-2
@@ -19,7 +19,7 @@ public class ConfigValueBuilder<V> {
|
||||
return fromSection(ConfigValueParser.required(), ConfigDataFunction.required());
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> fromSection(@NotNull ConfigValueParser<ConfigurationWrapper, V> valueParser,
|
||||
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);
|
||||
}
|
||||
@@ -46,7 +46,10 @@ public class ConfigValueBuilder<V> {
|
||||
return from(
|
||||
Object.class, ConfigDataFunction.identity(),
|
||||
ConfigValueParser.castObject(valueClass),
|
||||
ConfigDataFunction.identity(), ConfigDataFunction.toObject()
|
||||
s -> {
|
||||
if (s instanceof Enum<?>) return ((Enum<?>) s).name();
|
||||
else return s;
|
||||
}, ConfigDataFunction.toObject()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -1,6 +1,6 @@
|
||||
package cc.carm.lib.configuration.core.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
|
||||
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;
|
||||
@@ -10,16 +10,16 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Map;
|
||||
|
||||
public class SectionValueBuilder<V>
|
||||
extends AbstractConfigBuilder<SectionValueBuilder<V>, V> {
|
||||
extends CommonConfigBuilder<V, SectionValueBuilder<V>> {
|
||||
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected @NotNull ConfigValueParser<ConfigurationWrapper, V> parser;
|
||||
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 ConfigValueParser<ConfigurationWrapper<?>, V> parser,
|
||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
@@ -32,7 +32,11 @@ public class SectionValueBuilder<V>
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull SectionValueBuilder<V> parseValue(ConfigValueParser<ConfigurationWrapper, V> valueParser) {
|
||||
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;
|
||||
}
|
||||
@@ -44,11 +48,7 @@ public class SectionValueBuilder<V>
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredSection<V> build() {
|
||||
return new ConfiguredSection<>(
|
||||
this.provider, this.path, this.comments,
|
||||
this.valueClass, this.defaultValue,
|
||||
this.parser, this.serializer
|
||||
);
|
||||
return new ConfiguredSection<>(buildManifest(), this.valueClass, this.parser, this.serializer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+7
-4
@@ -1,12 +1,12 @@
|
||||
package cc.carm.lib.configuration.core.builder.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
|
||||
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 AbstractConfigBuilder<SourceValueBuilder<S, V>, V> {
|
||||
public class SourceValueBuilder<S, V> extends CommonConfigBuilder<V, SourceValueBuilder<S, V>> {
|
||||
|
||||
protected final @NotNull Class<S> sourceClass;
|
||||
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
|
||||
@@ -39,6 +39,10 @@ public class SourceValueBuilder<S, V> extends AbstractConfigBuilder<SourceValueB
|
||||
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;
|
||||
@@ -57,8 +61,7 @@ public class SourceValueBuilder<S, V> extends AbstractConfigBuilder<SourceValueB
|
||||
@Override
|
||||
public @NotNull ConfiguredValue<V> build() {
|
||||
return new ConfiguredValue<>(
|
||||
this.provider, this.path, this.comments,
|
||||
this.valueClass, this.defaultValue,
|
||||
buildManifest(), this.valueClass,
|
||||
this.valueParser.compose(this.sourceParser),
|
||||
this.valueSerializer.andThen(sourceSerializer)
|
||||
);
|
||||
|
||||
+102
-9
@@ -13,30 +13,34 @@ public interface ConfigDataFunction<T, R> {
|
||||
|
||||
default <V> @NotNull ConfigDataFunction<T, V> andThen(@NotNull ConfigDataFunction<? super R, V> after) {
|
||||
Objects.requireNonNull(after);
|
||||
return ((data) -> after.parse(parse(data)));
|
||||
return data -> after.parse(parse(data));
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, ? super T> identity() {
|
||||
return (input) -> input;
|
||||
static <T> @NotNull ConfigDataFunction<T, T> identity() {
|
||||
return input -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, T> identity(Class<T> type) {
|
||||
return input -> input;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T, V> @NotNull ConfigDataFunction<T, V> required() {
|
||||
return (input) -> {
|
||||
return input -> {
|
||||
throw new IllegalArgumentException("Please specify the value parser.");
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, Object> toObject() {
|
||||
return (input) -> input;
|
||||
return input -> input;
|
||||
}
|
||||
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigDataFunction<Object, V> castObject(Class<V> valueClass) {
|
||||
return (input) -> {
|
||||
return input -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
|
||||
};
|
||||
@@ -44,7 +48,7 @@ public interface ConfigDataFunction<T, R> {
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigDataFunction<String, V> castFromString(Class<V> valueClass) {
|
||||
return (input) -> {
|
||||
return input -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||
};
|
||||
@@ -52,12 +56,101 @@ public interface ConfigDataFunction<T, R> {
|
||||
|
||||
@Contract(pure = true)
|
||||
static <T> @NotNull ConfigDataFunction<T, String> castToString() {
|
||||
return (input) -> {
|
||||
return input -> {
|
||||
if (input instanceof String) return (String) input;
|
||||
else if (input instanceof Enum<?>) return ((Enum<?>) input).name();
|
||||
else return input.toString();
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static <V> @NotNull ConfigDataFunction<String, V> parseString(Class<V> valueClass) {
|
||||
return input -> {
|
||||
if (valueClass.isInstance(input)) return valueClass.cast(input);
|
||||
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Integer> intValue() {
|
||||
return input -> {
|
||||
if (input instanceof Integer) {
|
||||
return (Integer) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).intValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Integer.class.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Short> shortValue() {
|
||||
return input -> {
|
||||
if (input instanceof Short) {
|
||||
return (Short) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).shortValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Short.class.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Double> doubleValue() {
|
||||
return input -> {
|
||||
if (input instanceof Double) {
|
||||
return (Double) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).doubleValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Double.class.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Byte> byteValue() {
|
||||
return input -> {
|
||||
if (input instanceof Byte) {
|
||||
return (Byte) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).byteValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Byte.class.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Float> floatValue() {
|
||||
return input -> {
|
||||
if (input instanceof Float) {
|
||||
return (Float) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).floatValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Float.class.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Long> longValue() {
|
||||
return input -> {
|
||||
if (input instanceof Long) {
|
||||
return (Long) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).longValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Long.class.getName());
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigDataFunction<Object, Boolean> booleanValue() {
|
||||
return input -> {
|
||||
if (input instanceof Boolean) {
|
||||
return (Boolean) input;
|
||||
} else if (input instanceof String) {
|
||||
String s = (String) input;
|
||||
return Boolean.parseBoolean(s) || "yes".equalsIgnoreCase(s);
|
||||
} else if (input instanceof Integer) {
|
||||
return (Integer) input == 1;
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Boolean.class.getName());
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,27 @@ public interface ConfigValueParser<T, R> {
|
||||
@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());
|
||||
};
|
||||
@@ -71,91 +92,53 @@ public interface ConfigValueParser<T, R> {
|
||||
};
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull <T> ConfigValueParser<T, String> castToString(Class<T> clazz) {
|
||||
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) -> {
|
||||
if (input instanceof Integer) {
|
||||
return (Integer) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).intValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Integer.class.getName());
|
||||
};
|
||||
return (input, defaultValue) -> ConfigDataFunction.intValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Short> shortValue() {
|
||||
return (input, defaultValue) -> {
|
||||
if (input instanceof Short) {
|
||||
return (Short) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).shortValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Short.class.getName());
|
||||
};
|
||||
return (input, defaultValue) -> ConfigDataFunction.shortValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Double> doubleValue() {
|
||||
return (input, defaultValue) -> {
|
||||
if (input instanceof Double) {
|
||||
return (Double) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).doubleValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Double.class.getName());
|
||||
};
|
||||
return (input, defaultValue) -> ConfigDataFunction.doubleValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Byte> byteValue() {
|
||||
return (input, defaultValue) -> {
|
||||
if (input instanceof Byte) {
|
||||
return (Byte) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).byteValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Byte.class.getName());
|
||||
};
|
||||
return (input, defaultValue) -> ConfigDataFunction.byteValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Float> floatValue() {
|
||||
return (input, defaultValue) -> {
|
||||
if (input instanceof Float) {
|
||||
return (Float) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).floatValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Float.class.getName());
|
||||
};
|
||||
return (input, defaultValue) -> ConfigDataFunction.floatValue().parse(input);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
static @NotNull ConfigValueParser<Object, Long> longValue() {
|
||||
return (input, defaultValue) -> {
|
||||
if (input instanceof Long) {
|
||||
return (Long) input;
|
||||
} else if (input instanceof Number) {
|
||||
return ((Number) input).longValue();
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Long.class.getName());
|
||||
};
|
||||
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 Boolean) {
|
||||
return (Boolean) input;
|
||||
if (input instanceof Enum) {
|
||||
return enumClass.cast(input);
|
||||
} else if (input instanceof String) {
|
||||
String s = (String) input;
|
||||
return Boolean.parseBoolean(s) || "yes".equalsIgnoreCase(s);
|
||||
} else if (input instanceof Integer) {
|
||||
return ((Integer) input) == 1;
|
||||
} else throw new IllegalArgumentException("Cannot cast value to " + Boolean.class.getName());
|
||||
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());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+123
-10
@@ -1,38 +1,151 @@
|
||||
package cc.carm.lib.configuration.core.source;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.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;
|
||||
|
||||
public abstract class ConfigurationProvider {
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 配置文件提供者,用于为 {@link ConfigValue} 提供配置文件的源,以便实现读取、保存等操作。
|
||||
*
|
||||
* @param <W> 配置文件的原生功能类
|
||||
*/
|
||||
public abstract class ConfigurationProvider<W extends ConfigurationWrapper<?>> {
|
||||
|
||||
protected long updateTime;
|
||||
|
||||
public ConfigurationProvider() {
|
||||
protected ConfigurationProvider() {
|
||||
this.updateTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到配置文件的更新(最后加载)时间。
|
||||
*
|
||||
* @return 更新时间
|
||||
*/
|
||||
public long getUpdateTime() {
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用于 {@link CachedConfigValue} 判断缓存值是否过期(即缓存的时间早于配置文件的最后加载时间)。
|
||||
*
|
||||
* @param time 缓存值时的时间戳
|
||||
* @return 缓存值是否过期
|
||||
*/
|
||||
public boolean isExpired(long time) {
|
||||
return this.updateTime > time;
|
||||
return getUpdateTime() > time;
|
||||
}
|
||||
|
||||
public abstract @NotNull ConfigurationWrapper getConfiguration();
|
||||
/**
|
||||
* 得到配置文件的原生功能类。
|
||||
*
|
||||
* @return 原生类
|
||||
*/
|
||||
public abstract @NotNull W getConfiguration();
|
||||
|
||||
public abstract void reload() throws Exception;
|
||||
/**
|
||||
* 重载当前配置文件。(将不会保存已修改的内容)
|
||||
*
|
||||
* @throws Exception 当重载出现错误时抛出
|
||||
*/
|
||||
public void reload() throws Exception {
|
||||
onReload(); // 调用重写的Reload方法
|
||||
this.updateTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前对配置文件的更改进行保存。
|
||||
*
|
||||
* @throws Exception 当保存出现错误时抛出
|
||||
*/
|
||||
public abstract void save() throws Exception;
|
||||
|
||||
public abstract void setComments(@NotNull String path, @NotNull String... comments);
|
||||
/**
|
||||
* 针对于不同配置文件类型所执行的重载操作。
|
||||
*
|
||||
* @throws Exception 当操作出现错误时抛出。
|
||||
*/
|
||||
protected abstract void onReload() throws Exception;
|
||||
|
||||
public abstract @Nullable String[] getComments(@NotNull String path);
|
||||
public abstract @Nullable ConfigurationComments getComments();
|
||||
|
||||
public void initialize(Class<? extends ConfigurationRoot> configClazz) {
|
||||
ConfigInitializer.initialize(this, configClazz, true);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,10 +4,15 @@ 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();
|
||||
ConfigurationWrapper<?> getWrapper();
|
||||
|
||||
default boolean isBoolean(@NotNull String path) {
|
||||
return getWrapper().isType(path, Boolean.class);
|
||||
@@ -26,7 +31,6 @@ interface ConfigurationReader {
|
||||
return getWrapper().isType(path, Byte.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Byte getByte(@NotNull String path) {
|
||||
return getByte(path, (byte) 0);
|
||||
}
|
||||
@@ -36,12 +40,10 @@ interface ConfigurationReader {
|
||||
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);
|
||||
}
|
||||
@@ -56,7 +58,6 @@ interface ConfigurationReader {
|
||||
return getWrapper().isType(path, Integer.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Integer getInt(@NotNull String path) {
|
||||
return getInt(path, 0);
|
||||
}
|
||||
@@ -71,7 +72,6 @@ interface ConfigurationReader {
|
||||
return getWrapper().isType(path, Long.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Long getLong(@NotNull String path) {
|
||||
return getLong(path, 0L);
|
||||
}
|
||||
@@ -86,7 +86,6 @@ interface ConfigurationReader {
|
||||
return getWrapper().isType(path, Float.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Float getFloat(@NotNull String path) {
|
||||
return getFloat(path, 0.0F);
|
||||
}
|
||||
@@ -101,7 +100,6 @@ interface ConfigurationReader {
|
||||
return getWrapper().isType(path, Double.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Double getDouble(@NotNull String path) {
|
||||
return getDouble(path, 0.0D);
|
||||
}
|
||||
@@ -116,7 +114,6 @@ interface ConfigurationReader {
|
||||
return getWrapper().isType(path, Boolean.class);
|
||||
}
|
||||
|
||||
|
||||
default @Nullable Character getChar(@NotNull String path) {
|
||||
return getChar(path, null);
|
||||
}
|
||||
@@ -140,5 +137,58 @@ interface ConfigurationReader {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -9,13 +9,15 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ConfigurationWrapper extends ConfigurationReader{
|
||||
public interface ConfigurationWrapper<S> extends ConfigurationReader {
|
||||
|
||||
@Override
|
||||
default ConfigurationWrapper getWrapper() {
|
||||
default ConfigurationWrapper<S> getWrapper() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull S getSource();
|
||||
|
||||
@NotNull
|
||||
Set<String> getKeys(boolean deep);
|
||||
|
||||
@@ -66,7 +68,6 @@ public interface ConfigurationWrapper extends ConfigurationReader{
|
||||
boolean isConfigurationSection(@NotNull String path);
|
||||
|
||||
@Nullable
|
||||
ConfigurationWrapper getConfigurationSection(@NotNull String path);
|
||||
|
||||
ConfigurationWrapper<S> getConfigurationSection(@NotNull String path);
|
||||
|
||||
}
|
||||
|
||||
+19
-18
@@ -1,19 +1,24 @@
|
||||
package cc.carm.lib.configuration.core.source.impl;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.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 extends ConfigurationProvider {
|
||||
public abstract class FileConfigProvider<W extends ConfigurationWrapper<?>> extends ConfigurationProvider<W> {
|
||||
|
||||
protected final @NotNull File file;
|
||||
|
||||
public FileConfigProvider(@NotNull File file) {
|
||||
protected FileConfigProvider(@NotNull File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@@ -36,39 +41,35 @@ public abstract class FileConfigProvider extends ConfigurationProvider {
|
||||
if (sourcePath != null) {
|
||||
try {
|
||||
saveResource(sourcePath, true);
|
||||
} catch (Exception ignored) {
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void saveResource(@NotNull String resourcePath, boolean replace)
|
||||
throws NullPointerException, IOException, IllegalArgumentException {
|
||||
throws IOException, IllegalArgumentException {
|
||||
Objects.requireNonNull(resourcePath, "ResourcePath cannot be null");
|
||||
if (resourcePath.equals("")) throw new IllegalArgumentException("ResourcePath cannot be empty");
|
||||
if (resourcePath.isEmpty()) throw new IllegalArgumentException("ResourcePath cannot be empty");
|
||||
|
||||
resourcePath = resourcePath.replace('\\', '/');
|
||||
InputStream in = getResource(resourcePath);
|
||||
if (in == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists");
|
||||
|
||||
URL url = this.getClass().getClassLoader().getResource(resourcePath);
|
||||
if (url == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists");
|
||||
|
||||
int lastIndex = resourcePath.lastIndexOf('/');
|
||||
File outDir = new File(file, resourcePath.substring(0, Math.max(lastIndex, 0)));
|
||||
File outDir = file.getParentFile();
|
||||
|
||||
if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir);
|
||||
if (!file.exists() || replace) {
|
||||
try {
|
||||
|
||||
OutputStream out = new FileOutputStream(file);
|
||||
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);
|
||||
}
|
||||
out.close();
|
||||
in.close();
|
||||
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
package cc.carm.lib.configuration.core.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
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;
|
||||
|
||||
public CachedConfigValue(@Nullable ConfigurationProvider provider, @Nullable String sectionPath,
|
||||
@NotNull String[] comments, @Nullable T defaultValue) {
|
||||
super(provider, sectionPath, comments, defaultValue);
|
||||
}
|
||||
|
||||
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 useDefault() {
|
||||
return useOrDefault(null);
|
||||
}
|
||||
|
||||
protected final T useOrDefault(@Nullable T value) {
|
||||
return updateCache(this.defaultValue == null ? value : this.defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,58 +2,67 @@ package cc.carm.lib.configuration.core.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
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.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class ConfigValue<T> {
|
||||
public abstract class ConfigValue<T> extends ValueManifest<T> {
|
||||
|
||||
public static @NotNull ConfigBuilder builder() {
|
||||
return new ConfigBuilder();
|
||||
}
|
||||
|
||||
protected @Nullable T defaultValue;
|
||||
|
||||
protected @Nullable ConfigurationProvider provider;
|
||||
protected @Nullable String configPath;
|
||||
protected @NotNull String[] comments;
|
||||
|
||||
public ConfigValue(@Nullable ConfigurationProvider provider, @Nullable String configPath,
|
||||
@NotNull String[] comments, @Nullable T defaultValue) {
|
||||
this.provider = provider;
|
||||
this.configPath = configPath;
|
||||
this.comments = comments;
|
||||
|
||||
this.defaultValue = defaultValue;
|
||||
protected ConfigValue(@NotNull ValueManifest<T> manifest) {
|
||||
super(manifest.provider, manifest.configPath, manifest.headerComments, manifest.inlineComment, manifest.defaultValue);
|
||||
}
|
||||
|
||||
public void initialize(@NotNull ConfigurationProvider provider, @NotNull String configPath,
|
||||
@NotNull String... comments) {
|
||||
if (this.provider == null) this.provider = provider;
|
||||
if (this.configPath == null) this.configPath = configPath;
|
||||
if (this.comments.length == 0) this.comments = comments;
|
||||
|
||||
this.provider.setComments(this.configPath, this.comments);
|
||||
get();
|
||||
/**
|
||||
* @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 @Nullable T getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public void setDefaultValue(@Nullable T defaultValue) {
|
||||
this.defaultValue = 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 Optional.ofNullable(get()).orElse(getDefaultValue());
|
||||
return getOptional().orElse(getDefaultValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到该配置的非空值。
|
||||
*
|
||||
* @return 非空值
|
||||
* @throws NullPointerException 对应数据为空时抛出
|
||||
*/
|
||||
public @NotNull T getNotNull() {
|
||||
return Objects.requireNonNull(getOrDefault(), "Value(" + configPath + ") is null.");
|
||||
}
|
||||
@@ -62,44 +71,44 @@ public abstract class ConfigValue<T> {
|
||||
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);
|
||||
}
|
||||
|
||||
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() {
|
||||
return getConfiguration().get(getConfigPath());
|
||||
}
|
||||
|
||||
protected void setValue(@Nullable Object value) {
|
||||
getConfiguration().set(getConfigPath(), value);
|
||||
}
|
||||
|
||||
public String[] getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public void setComments(String[] comments) {
|
||||
this.comments = comments;
|
||||
/**
|
||||
* 判断加载的配置是否与默认值相同。
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,31 +2,39 @@ 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.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.value.CachedConfigValue;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
|
||||
public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements List<V> {
|
||||
|
||||
public static <V> @NotNull ConfigListBuilder<V> builder(@NotNull Class<V> valueClass) {
|
||||
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(@Nullable ConfigurationProvider provider,
|
||||
@Nullable String sectionPath, @NotNull String[] comments,
|
||||
@NotNull Class<V> valueClass, @Nullable List<V> defaultValue,
|
||||
public ConfiguredList(@NotNull ValueManifest<List<V>> manifest, @NotNull Class<V> valueClass,
|
||||
@NotNull ConfigDataFunction<Object, V> parser,
|
||||
@NotNull ConfigDataFunction<V, Object> serializer) {
|
||||
super(provider, sectionPath, comments, defaultValue);
|
||||
super(manifest);
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
@@ -34,12 +42,12 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
|
||||
|
||||
@Override
|
||||
public @NotNull List<V> get() {
|
||||
if (isExpired()) { // 已过时的数据,需要重新解析一次。
|
||||
if (!isExpired()) return getCachedOrDefault(new ArrayList<>());
|
||||
// Data that is outdated and needs to be parsed again.
|
||||
List<V> list = new ArrayList<>();
|
||||
|
||||
List<?> data = getConfiguration().getList(getConfigPath());
|
||||
if (data == null || data.isEmpty()) return useOrDefault(list);
|
||||
|
||||
List<?> data = getConfiguration().contains(getConfigPath()) ?
|
||||
getConfiguration().getList(getConfigPath()) : null;
|
||||
if (data == null) return getDefaultFirst(list);
|
||||
for (Object dataVal : data) {
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
@@ -48,11 +56,30 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return updateCache(list);
|
||||
} else if (getCachedValue() != null) return getCachedValue();
|
||||
else if (getDefaultValue() != null) return getDefaultValue();
|
||||
else return new ArrayList<>();
|
||||
}
|
||||
|
||||
@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
|
||||
@@ -73,5 +100,121 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
|
||||
}
|
||||
}
|
||||
|
||||
@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,125 +1,28 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
import cc.carm.lib.configuration.core.builder.map.ConfigMapBuilder;
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.value.CachedConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.core.value.impl.ConfigValueMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> {
|
||||
public class ConfiguredMap<K, V> extends ConfigValueMap<K, V, Object> {
|
||||
|
||||
public static <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> builder(@NotNull Class<K> keyClass,
|
||||
@NotNull Class<V> valueClass) {
|
||||
return builder().asMap(keyClass, valueClass);
|
||||
}
|
||||
|
||||
protected final @NotNull Supplier<? extends Map<K, V>> supplier;
|
||||
|
||||
protected final @NotNull Class<K> keyClass;
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
|
||||
protected final @NotNull ConfigDataFunction<String, K> keyParser;
|
||||
protected final @NotNull ConfigDataFunction<Object, V> valueParser;
|
||||
|
||||
protected final @NotNull ConfigDataFunction<K, String> keySerializer;
|
||||
protected final @NotNull ConfigDataFunction<V, Object> valueSerializer;
|
||||
|
||||
public ConfiguredMap(@Nullable ConfigurationProvider provider,
|
||||
@Nullable String sectionPath, @NotNull String[] comments,
|
||||
@Nullable Map<K, V> defaultValue, @NotNull Supplier<? extends Map<K, V>> supplier,
|
||||
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(provider, sectionPath, comments, defaultValue);
|
||||
this.supplier = supplier;
|
||||
this.keyClass = keyClass;
|
||||
this.valueClass = valueClass;
|
||||
this.keyParser = keyParser;
|
||||
this.valueParser = valueParser;
|
||||
this.keySerializer = keySerializer;
|
||||
this.valueSerializer = valueSerializer;
|
||||
}
|
||||
|
||||
public @NotNull Class<K> getKeyClass() {
|
||||
return keyClass;
|
||||
}
|
||||
|
||||
public @NotNull Class<V> getValueClass() {
|
||||
return valueClass;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<String, K> getKeyParser() {
|
||||
return keyParser;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<Object, V> getValueParser() {
|
||||
return valueParser;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<K, String> getKeySerializer() {
|
||||
return keySerializer;
|
||||
}
|
||||
|
||||
public @NotNull ConfigDataFunction<V, Object> getValueSerializer() {
|
||||
return valueSerializer;
|
||||
super(manifest, Object.class, mapObjSupplier, keyClass, keyParser, valueClass, valueParser, keySerializer, valueSerializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<K, V> get() {
|
||||
if (isExpired()) { // 已过时的数据,需要重新解析一次。
|
||||
Map<K, V> map = supplier.get();
|
||||
|
||||
ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath());
|
||||
if (section == null) return useOrDefault(map);
|
||||
|
||||
Set<String> keys = section.getKeys(false);
|
||||
if (keys.isEmpty()) return useOrDefault(map);
|
||||
|
||||
for (String dataKey : keys) {
|
||||
Object dataVal = section.get(dataKey);
|
||||
if (dataVal == null) continue;
|
||||
try {
|
||||
K key = keyParser.parse(dataKey);
|
||||
V value = valueParser.parse(dataVal);
|
||||
map.put(key, value);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
public Object getSource(ConfigurationWrapper<?> section, String dataKey) {
|
||||
return section.get(dataKey);
|
||||
}
|
||||
}
|
||||
|
||||
return updateCache(map);
|
||||
} else if (getCachedValue() != null) return getCachedValue();
|
||||
else if (getDefaultValue() != null) return getDefaultValue();
|
||||
else return supplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(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 {
|
||||
String key = keySerializer.parse(entry.getKey());
|
||||
Object val = valueSerializer.parse(entry.getValue());
|
||||
data.put(key, val);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
setValue(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+37
-21
@@ -1,68 +1,85 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
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.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.value.CachedConfigValue;
|
||||
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;
|
||||
import java.util.Optional;
|
||||
|
||||
public class ConfiguredSection<V> extends CachedConfigValue<V> {
|
||||
|
||||
public static <V> @NotNull SectionValueBuilder<V> builder(@NotNull Class<V> valueClass) {
|
||||
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 ConfigValueParser<ConfigurationWrapper<?>, V> parser;
|
||||
protected final @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
|
||||
|
||||
public ConfiguredSection(@Nullable ConfigurationProvider provider,
|
||||
@Nullable String sectionPath, @NotNull String[] comments,
|
||||
@NotNull Class<V> valueClass, @Nullable V defaultValue,
|
||||
@NotNull ConfigValueParser<ConfigurationWrapper, V> parser,
|
||||
public ConfiguredSection(@NotNull ValueManifest<V> manifest, @NotNull Class<V> valueClass,
|
||||
@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser,
|
||||
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
|
||||
super(provider, sectionPath, comments, defaultValue);
|
||||
super(manifest);
|
||||
this.valueClass = valueClass;
|
||||
this.parser = parser;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value's type class
|
||||
*/
|
||||
public @NotNull Class<V> getValueClass() {
|
||||
return valueClass;
|
||||
}
|
||||
|
||||
public @NotNull ConfigValueParser<ConfigurationWrapper, V> getParser() {
|
||||
/**
|
||||
* @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()) { // 已过时的数据,需要重新解析一次。
|
||||
ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath());
|
||||
if (section == null) return useDefault();
|
||||
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 useDefault();
|
||||
}
|
||||
} else return Optional.ofNullable(getCachedValue()).orElse(defaultValue);
|
||||
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);
|
||||
@@ -70,7 +87,6 @@ public class ConfiguredSection<V> extends CachedConfigValue<V> {
|
||||
else {
|
||||
try {
|
||||
setValue(serializer.parse(value));
|
||||
// getConfiguration().createSection(getSectionPath(), serializer.parse(value));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
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,28 +1,30 @@
|
||||
package cc.carm.lib.configuration.core.value.type;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
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.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.value.CachedConfigValue;
|
||||
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.Optional;
|
||||
|
||||
public class ConfiguredValue<V> extends CachedConfigValue<V> {
|
||||
|
||||
public static <V> ConfigValueBuilder<V> builder(Class<V> valueClass) {
|
||||
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 builder(valueClass).fromObject().defaults(defaultValue).build();
|
||||
return builderOf(valueClass).fromObject().defaults(defaultValue).build();
|
||||
}
|
||||
|
||||
protected final @NotNull Class<V> valueClass;
|
||||
@@ -30,42 +32,60 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
|
||||
protected final @NotNull ConfigValueParser<Object, V> parser;
|
||||
protected final @NotNull ConfigDataFunction<V, Object> serializer;
|
||||
|
||||
public ConfiguredValue(@Nullable ConfigurationProvider provider,
|
||||
@Nullable String sectionPath, @NotNull String[] comments,
|
||||
@NotNull Class<V> valueClass, @Nullable V defaultValue,
|
||||
public ConfiguredValue(@NotNull ValueManifest<V> manifest, @NotNull Class<V> valueClass,
|
||||
@NotNull ConfigValueParser<Object, V> parser,
|
||||
@NotNull ConfigDataFunction<V, Object> serializer) {
|
||||
|
||||
super(provider, sectionPath, comments, defaultValue);
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get() {
|
||||
if (isExpired()) { // 已过时的数据,需要重新解析一次。
|
||||
Object value = getConfiguration().get(getConfigPath());
|
||||
if (value == null) return useDefault(); // 获取的值不存在,直接使用默认值。
|
||||
try {
|
||||
// 若未出现错误,则直接更新缓存并返回。
|
||||
return updateCache(this.parser.parse(value, this.defaultValue));
|
||||
} catch (Exception e) {
|
||||
// 出现了解析错误,提示并返回默认值。
|
||||
e.printStackTrace();
|
||||
return useDefault();
|
||||
}
|
||||
} else return Optional.ofNullable(getCachedValue()).orElse(defaultValue);
|
||||
/**
|
||||
* @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);
|
||||
|
||||
@@ -7,10 +7,10 @@ public class NameTest {
|
||||
@Test
|
||||
public void onTest() {
|
||||
|
||||
System.out.println(ConfigInitializer.getSectionName("LoveGames")); // -> love-games
|
||||
System.out.println(ConfigInitializer.getSectionName("EASY_GAME")); // -> easy-game
|
||||
System.out.println(ConfigInitializer.getSectionName("F")); //-? f
|
||||
System.out.println(ConfigInitializer.getSectionName("Test123123")); // -? test123123123
|
||||
System.out.println(ConfigInitializer.getPathFromName("LoveGames")); // -> love-games
|
||||
System.out.println(ConfigInitializer.getPathFromName("EASY_GAME")); // -> easy-game
|
||||
System.out.println(ConfigInitializer.getPathFromName("F")); //-? f
|
||||
System.out.println(ConfigInitializer.getPathFromName("Test123123")); // -? test123123123
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package cc.carm.test.config.offset;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Chris2018998
|
||||
*/
|
||||
public class FieldOffset implements Comparable<FieldOffset> {
|
||||
private final Field field;
|
||||
private Long offsetValue;
|
||||
private List<FieldOffset> subFieldOffsetList;
|
||||
|
||||
public FieldOffset(Field field) {
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public Long getOffsetValue() {
|
||||
return offsetValue;
|
||||
}
|
||||
|
||||
public void setOffsetValue(Long offsetValue) {
|
||||
this.offsetValue = offsetValue;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (subFieldOffsetList == null)
|
||||
return field.getName();
|
||||
else {
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
|
||||
for (int i = 0; i < subFieldOffsetList.size(); i++) {
|
||||
FieldOffset offset = subFieldOffsetList.get(i);
|
||||
if (i > 0) builder.append(",");
|
||||
builder.append(field.getName()).append(".").append(offset.toString());
|
||||
}
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public List<FieldOffset> getSubFieldOffsetList() {
|
||||
return subFieldOffsetList;
|
||||
}
|
||||
|
||||
public void setSubFieldOffsetList(List<FieldOffset> subFieldOffsetList) {
|
||||
this.subFieldOffsetList = subFieldOffsetList;
|
||||
}
|
||||
|
||||
public Field getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull FieldOffset that) {
|
||||
return this.offsetValue.compareTo(that.offsetValue);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
//package config.offset;
|
||||
//
|
||||
//
|
||||
//import sun.misc.Unsafe;
|
||||
//
|
||||
//import java.lang.reflect.Field;
|
||||
//import java.lang.reflect.Modifier;
|
||||
//import java.util.Collections;
|
||||
//import java.util.LinkedList;
|
||||
//import java.util.List;
|
||||
//
|
||||
///**
|
||||
// * @author Chris2018998
|
||||
// */
|
||||
//public class OffsetUtil {
|
||||
// private static Unsafe unsafe;
|
||||
//
|
||||
// static {
|
||||
// try {
|
||||
// // 获取 Unsafe 内部的私有的实例化单例对象
|
||||
// Field field = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
// // 无视权限
|
||||
// field.setAccessible(true);
|
||||
// unsafe = (Unsafe) field.get(null);
|
||||
// } catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
//// try {
|
||||
//// unsafe = AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> {
|
||||
//// Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
//// theUnsafe.setAccessible(true);
|
||||
//// return (Unsafe) theUnsafe.get(null);
|
||||
//// });
|
||||
//// } catch (Throwable e) {
|
||||
//// System.err.println("Unable to load unsafe");
|
||||
//// }
|
||||
// }
|
||||
//
|
||||
// public static List<FieldOffset> getClassMemberOffset(Class<?> beanClass) {
|
||||
// List<FieldOffset> offsetsList = new LinkedList<>();
|
||||
// for (Field field : beanClass.getDeclaredFields()) {
|
||||
// FieldOffset fieldOffset = new FieldOffset(field);
|
||||
// offsetsList.add(fieldOffset);
|
||||
// if (Modifier.isStatic(field.getModifiers()))
|
||||
// fieldOffset.setOffsetValue(unsafe.staticFieldOffset(field));
|
||||
// else
|
||||
// fieldOffset.setOffsetValue(unsafe.objectFieldOffset(field));
|
||||
// Class<?> fieldType = field.getType();
|
||||
// if (!fieldType.getName().startsWith("java")) {
|
||||
// Field[] subfields = fieldType.getDeclaredFields();
|
||||
// if (subfields.length > 0) {
|
||||
// fieldOffset.setSubFieldOffsetList(getClassMemberOffset(fieldType));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Collections.sort(offsetsList);
|
||||
// return offsetsList;
|
||||
// }
|
||||
//
|
||||
//
|
||||
//}
|
||||
//
|
||||
@@ -5,16 +5,16 @@
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>1.0.6</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
<version>3.9.0</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
|
||||
<artifactId>easyconfiguration-spigot</artifactId>
|
||||
<artifactId>easyconfiguration-demo</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
@@ -26,13 +26,6 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.18.2-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -56,5 +49,4 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
+8
-22
@@ -1,28 +1,19 @@
|
||||
package config.source;
|
||||
package cc.carm.lib.configuration.demo;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigComment;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
|
||||
@ConfigPath("database")
|
||||
@ConfigComment({"数据库配置", " 用于提供数据库连接,进行数据库操作。"})
|
||||
public class DemoConfiguration extends ConfigurationRoot {
|
||||
|
||||
@ConfigPath(root = true)
|
||||
@ConfigComment({
|
||||
"有时候,需要在配置文件最上面显示点东西,",
|
||||
"此时就推荐添加一个可以用到但并不重要的参数到最上面",
|
||||
"并给他添加对应的注释。"
|
||||
})
|
||||
protected static final ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
|
||||
|
||||
@HeaderComment({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
|
||||
public class DatabaseConfiguration extends ConfigurationRoot {
|
||||
|
||||
@ConfigPath("driver")
|
||||
@ConfigComment({
|
||||
@HeaderComment({
|
||||
"数据库驱动配置,请根据数据库类型设置。",
|
||||
"- MySQL: com.mysql.cj.jdbc.Driver",
|
||||
"- MySQL(旧): com.mysql.jdbc.Driver",
|
||||
"- MySQL(新): com.mysql.cj.jdbc.Driver",
|
||||
"- MariaDB(推荐): org.mariadb.jdbc.Driver",
|
||||
})
|
||||
protected static final ConfigValue<String> DRIVER_NAME = ConfiguredValue.of(
|
||||
@@ -31,18 +22,13 @@ public class DemoConfiguration extends ConfigurationRoot {
|
||||
|
||||
protected static final ConfigValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
|
||||
protected static final ConfigValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
|
||||
|
||||
protected static final ConfigValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
|
||||
protected static final ConfigValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
|
||||
protected static final ConfigValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
|
||||
|
||||
protected static final ConfigValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
|
||||
|
||||
protected static String buildJDBC() {
|
||||
return String.format("jdbc:mysql://%s:%s/%s%s",
|
||||
HOST.get(), PORT.get(), DATABASE.get(), EXTRA.get()
|
||||
);
|
||||
return String.format("jdbc:mysql://%s:%s/%s%s", HOST.get(), PORT.get(), DATABASE.get(), EXTRA.get());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package cc.carm.lib.configuration.demo.tests;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.conf.TestConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class ConfigurationTest {
|
||||
|
||||
@TestOnly
|
||||
public static void testDemo(ConfigurationProvider<?> provider) {
|
||||
provider.initialize(DemoConfiguration.class);
|
||||
|
||||
System.out.println("----------------------------------------------------");
|
||||
|
||||
System.out.println("Test Number: ");
|
||||
|
||||
System.out.println("before: " + DemoConfiguration.TEST_NUMBER.get());
|
||||
DemoConfiguration.TEST_NUMBER.set((long) (Long.MAX_VALUE * Math.random()));
|
||||
System.out.println("after: " + DemoConfiguration.TEST_NUMBER.get());
|
||||
|
||||
System.out.println("> Test Value:");
|
||||
System.out.println("before: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get());
|
||||
DemoConfiguration.Sub.UUID_CONFIG_VALUE.set(UUID.randomUUID());
|
||||
System.out.println("after: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get());
|
||||
|
||||
System.out.println("> Test List:");
|
||||
|
||||
System.out.println(" Before:");
|
||||
DemoConfiguration.Sub.That.OPERATORS.forEach(System.out::println);
|
||||
List<UUID> operators = IntStream.range(0, 5).mapToObj(i -> UUID.randomUUID()).collect(Collectors.toList());
|
||||
DemoConfiguration.Sub.That.OPERATORS.set(operators);
|
||||
System.out.println(" After:");
|
||||
DemoConfiguration.Sub.That.OPERATORS.forEach(System.out::println);
|
||||
|
||||
System.out.println("> Clear List:");
|
||||
System.out.println(" Before: size :" + DemoConfiguration.Sub.That.OPERATORS.size());
|
||||
DemoConfiguration.Sub.That.OPERATORS.modify(List::clear);
|
||||
System.out.println(" After size :" + DemoConfiguration.Sub.That.OPERATORS.size());
|
||||
|
||||
System.out.println("> Test Section:");
|
||||
System.out.println(DemoConfiguration.MODEL_TEST.get());
|
||||
DemoConfiguration.MODEL_TEST.set(TestModel.random());
|
||||
|
||||
System.out.println("> Test Maps:");
|
||||
DemoConfiguration.USERS.forEach((k, v) -> System.out.println(k + ": " + v));
|
||||
LinkedHashMap<Integer, UUID> data = new LinkedHashMap<>();
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
data.put(i, UUID.randomUUID());
|
||||
}
|
||||
DemoConfiguration.USERS.set(data);
|
||||
System.out.println("----------------------------------------------------");
|
||||
}
|
||||
|
||||
public static void testInner(ConfigurationProvider<?> provider) {
|
||||
|
||||
TestConfiguration TEST = new TestConfiguration();
|
||||
|
||||
provider.initialize(TEST, true);
|
||||
|
||||
System.out.println("> Test Inner value before:");
|
||||
System.out.println(TEST.INNER.INNER_VALUE.getNotNull());
|
||||
|
||||
double after = Math.random() * 200D;
|
||||
System.out.println("> Test Inner value -> " + after);
|
||||
TEST.INNER.INNER_VALUE.set(after);
|
||||
|
||||
System.out.println("> Test Inner value after:");
|
||||
System.out.println(TEST.INNER.INNER_VALUE.getNotNull());
|
||||
|
||||
}
|
||||
|
||||
public static void save(ConfigurationProvider<?> provider) {
|
||||
try {
|
||||
provider.save();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.Configuration;
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.annotation.InlineComment;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
||||
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
|
||||
@HeaderComment({"此处内容将显示在配置文件的最上方"})
|
||||
public interface DemoConfiguration extends Configuration {
|
||||
|
||||
@ConfigPath(root = true)
|
||||
ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
|
||||
|
||||
@ConfigPath(root = true)
|
||||
ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(1000000L);
|
||||
|
||||
ConfigValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS);
|
||||
|
||||
// 支持通过 Class<?> 变量标注子配置,一并注册。
|
||||
// 注意: 若对应类也有注解,则优先使用类的注解。
|
||||
@ConfigPath("other-class-config") //支持通过注解修改子配置的主路径,若不修改则以变量名自动生成。
|
||||
@HeaderComment({"", "Something..."}) // 支持给子路径直接打注释
|
||||
@InlineComment("InlineComments for class path")
|
||||
Class<?> OTHER = OtherConfiguration.class;
|
||||
|
||||
@ConfigPath("user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
||||
@InlineComment("Section数据也支持InlineComment注释")
|
||||
ConfigValue<TestModel> MODEL_TEST = ConfiguredSection.builderOf(TestModel.class)
|
||||
.defaults(new TestModel("Carm", UUID.randomUUID()))
|
||||
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
|
||||
.serializeValue(TestModel::serialize).build();
|
||||
|
||||
@HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"})
|
||||
ConfiguredMap<Integer, UUID> USERS = ConfiguredMap.builderOf(Integer.class, UUID.class)
|
||||
.asLinkedMap().fromString()
|
||||
.parseKey(Integer::parseInt)
|
||||
.parseValue(v -> Objects.requireNonNull(UUID.fromString(v)))
|
||||
.build();
|
||||
|
||||
|
||||
/**
|
||||
* 支持内部类的直接注册。
|
||||
* 注意,需要使用 {@link ConfigInitializer#initialize(Class, boolean, boolean)} 方法,并设定第三个参数为 true。
|
||||
*/
|
||||
class Sub extends ConfigurationRoot {
|
||||
|
||||
@ConfigPath(value = "uuid-value", root = true)
|
||||
@InlineComment("This is an inline comment")
|
||||
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
|
||||
.builderOf(UUID.class).fromString()
|
||||
.parseValue((data, defaultValue) -> UUID.fromString(data))
|
||||
.build();
|
||||
|
||||
public static class That extends ConfigurationRoot {
|
||||
|
||||
public static final ConfiguredList<UUID> OPERATORS = ConfiguredList
|
||||
.builderOf(UUID.class).fromString()
|
||||
.parseValue(s -> Objects.requireNonNull(UUID.fromString(s)))
|
||||
.build();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
public class OtherConfiguration {
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.annotation.InlineComment;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
import cc.carm.lib.configuration.demo.tests.model.TestModel;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class TestConfiguration extends ConfigurationRoot {
|
||||
|
||||
public final TestInnerConfiguration INNER = new TestInnerConfiguration();
|
||||
|
||||
public final ConfigValue<Double> CLASS_VALUE = ConfiguredValue.of(Double.class, 1.0D);
|
||||
|
||||
@ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
|
||||
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
|
||||
@InlineComment("Section数据也支持InlineComment注释")
|
||||
public final ConfigValue<TestModel> TEST_MODEL = ConfiguredSection
|
||||
.builderOf(TestModel.class)
|
||||
.defaults(new TestModel("Carm", UUID.randomUUID()))
|
||||
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
|
||||
.serializeValue(TestModel::serialize).build();
|
||||
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package cc.carm.lib.configuration.demo.tests.conf;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
|
||||
@HeaderComment("Inner Test")
|
||||
public class TestInnerConfiguration extends ConfigurationRoot {
|
||||
|
||||
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(Double.class, 1.0D);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package cc.carm.lib.configuration.demo.tests.model;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class AbstractModel {
|
||||
|
||||
protected final @NotNull String name;
|
||||
|
||||
public AbstractModel(@NotNull String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public @NotNull String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
+11
-15
@@ -1,4 +1,4 @@
|
||||
package config.misc;
|
||||
package cc.carm.lib.configuration.demo.tests.model;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -7,28 +7,19 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TestUser {
|
||||
public class TestModel extends AbstractModel {
|
||||
|
||||
public String name;
|
||||
public UUID uuid;
|
||||
|
||||
public TestUser(String name, UUID uuid) {
|
||||
this.name = name;
|
||||
public TestModel(String name, UUID uuid) {
|
||||
super(name);
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setUuid(UUID uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public UUID getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
@@ -42,14 +33,19 @@ public class TestUser {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static TestUser deserialize(ConfigurationWrapper section) throws Exception {
|
||||
public static TestModel deserialize(ConfigurationWrapper<?> section) {
|
||||
String name = section.getString("name");
|
||||
if (name == null) throw new NullPointerException("name is null");
|
||||
String uuidString = section.getString("info.uuid");
|
||||
if (uuidString == null) throw new NullPointerException("uuid is null");
|
||||
return new TestUser(name, UUID.fromString(uuidString));
|
||||
return new TestModel(name, UUID.fromString(uuidString));
|
||||
}
|
||||
|
||||
public static TestModel random() {
|
||||
return new TestModel(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TestUser{" +
|
||||
@@ -2,23 +2,23 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>1.0.6</version>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<version>3.9.0</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
|
||||
<artifactId>easyconfiguration-bungee</artifactId>
|
||||
<artifactId>easyconfiguration-hocon</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
@@ -27,21 +27,18 @@
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.18-R0.1-SNAPSHOT</version>
|
||||
<type>jar</type>
|
||||
<scope>provided</scope>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-demo</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.18-R0.1-SNAPSHOT</version>
|
||||
<type>javadoc</type>
|
||||
<scope>provided</scope>
|
||||
<groupId>com.typesafe</groupId>
|
||||
<artifactId>config</artifactId>
|
||||
<version>1.4.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -65,4 +62,5 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
</project>
|
||||
+8
-7
@@ -1,15 +1,17 @@
|
||||
package cc.carm.lib.configuration;
|
||||
|
||||
import cc.carm.lib.configuration.spigot.SpigotConfigProvider;
|
||||
import cc.carm.lib.configuration.hocon.HOCONFileConfigProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class EasyConfiguration {
|
||||
|
||||
private EasyConfiguration() {
|
||||
}
|
||||
|
||||
public static SpigotConfigProvider from(File file, String source) {
|
||||
SpigotConfigProvider provider = new SpigotConfigProvider(file);
|
||||
public static HOCONFileConfigProvider from(File file, String source) {
|
||||
HOCONFileConfigProvider provider = new HOCONFileConfigProvider(file);
|
||||
try {
|
||||
provider.initializeFile(source);
|
||||
provider.initializeConfig();
|
||||
@@ -19,17 +21,16 @@ public class EasyConfiguration {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public static SpigotConfigProvider from(File file) {
|
||||
public static HOCONFileConfigProvider from(File file) {
|
||||
return from(file, file.getName());
|
||||
}
|
||||
|
||||
public static SpigotConfigProvider from(String fileName) {
|
||||
public static HOCONFileConfigProvider from(String fileName) {
|
||||
return from(fileName, fileName);
|
||||
}
|
||||
|
||||
public static SpigotConfigProvider from(String fileName, String source) {
|
||||
public static HOCONFileConfigProvider from(String fileName, String source) {
|
||||
return from(new File(fileName), source);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package cc.carm.lib.configuration.hocon;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.hocon.util.HOCONUtils;
|
||||
import com.typesafe.config.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class HOCONConfigWrapper implements ConfigurationWrapper<Map<String, Object>> {
|
||||
private static final char SEPARATOR = '.';
|
||||
protected final Map<String, Object> data;
|
||||
|
||||
public HOCONConfigWrapper(ConfigObject config) {
|
||||
this.data = new LinkedHashMap<>();
|
||||
|
||||
config.forEach((key, value) -> {
|
||||
Config cfg = config.toConfig();
|
||||
ConfigValue cv = cfg.getValue(key);
|
||||
if (cv.valueType() == ConfigValueType.OBJECT) {
|
||||
HOCONConfigWrapper.this.data.put(key, new HOCONConfigWrapper((ConfigObject) cv));
|
||||
} else {
|
||||
HOCONConfigWrapper.this.data.put(key, value.unwrapped());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getSource() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<String> getKeys(boolean deep) {
|
||||
return this.getValues(deep).keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
return HOCONUtils.getKeysFromObject(this, deep, "").stream().collect(
|
||||
LinkedHashMap::new,
|
||||
(map, key) -> map.put(key, get(key)),
|
||||
LinkedHashMap::putAll
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
if (value instanceof Map) {
|
||||
//noinspection unchecked
|
||||
value = new HOCONConfigWrapper(ConfigFactory.parseMap((Map<String, ?>) value).root());
|
||||
}
|
||||
|
||||
HOCONConfigWrapper section = HOCONUtils.getObjectOn(this, path, SEPARATOR);
|
||||
String simplePath = HOCONUtils.getSimplePath(path, SEPARATOR);
|
||||
|
||||
if (value == null) {
|
||||
section.data.remove(simplePath);
|
||||
} else {
|
||||
section.setDirect(simplePath, value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能设置当前路径下的内容
|
||||
* 避免环回
|
||||
*
|
||||
* @param path 路径
|
||||
*/
|
||||
public void setDirect(@NotNull String path, @Nullable Object value) {
|
||||
this.data.put(path, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path) {
|
||||
return this.get(path) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
HOCONConfigWrapper section = HOCONUtils.getObjectOn(this, path, SEPARATOR);
|
||||
return section.getDirect(HOCONUtils.getSimplePath(path, SEPARATOR));
|
||||
}
|
||||
|
||||
/**
|
||||
* 只能获取当前路径下的内容
|
||||
* 避免环回
|
||||
*
|
||||
* @param path 路径
|
||||
*/
|
||||
public Object getDirect(@NotNull String path) {
|
||||
return this.data.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList(@NotNull String path) {
|
||||
return this.get(path) instanceof List<?>;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<?> getList(@NotNull String path) {
|
||||
Object val = this.get(path);
|
||||
return (val instanceof List<?>) ? (List<?>) val : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurationSection(@NotNull String path) {
|
||||
return this.get(path) instanceof HOCONConfigWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigurationWrapper<Map<String, Object>> getConfigurationSection(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof HOCONConfigWrapper) ? (HOCONConfigWrapper) val : null;
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
package cc.carm.lib.configuration.hocon;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||
import cc.carm.lib.configuration.hocon.exception.HOCONGetValueException;
|
||||
import cc.carm.lib.configuration.hocon.util.HOCONUtils;
|
||||
import com.typesafe.config.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class HOCONFileConfigProvider extends FileConfigProvider<HOCONConfigWrapper> {
|
||||
protected final @NotNull ConfigurationComments comments = new ConfigurationComments();
|
||||
protected HOCONConfigWrapper configuration;
|
||||
protected ConfigInitializer<HOCONFileConfigProvider> initializer;
|
||||
|
||||
public HOCONFileConfigProvider(@NotNull File file) {
|
||||
super(file);
|
||||
this.initializer = new ConfigInitializer<>(this);
|
||||
}
|
||||
|
||||
public void initializeConfig() {
|
||||
try {
|
||||
this.configuration = new HOCONConfigWrapper(ConfigFactory.parseFile(this.file, ConfigParseOptions.defaults()
|
||||
.setSyntax(ConfigSyntax.CONF)
|
||||
.setAllowMissing(false)).root());
|
||||
} catch (ConfigException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull HOCONConfigWrapper getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws IOException {
|
||||
Files.write(this.file.toPath(), HOCONUtils.renderWithComment(configuration, comments::getHeaderComment).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReload() throws ConfigException {
|
||||
ConfigObject conf = ConfigFactory.parseFile(this.file, ConfigParseOptions.defaults()
|
||||
.setSyntax(ConfigSyntax.CONF)
|
||||
.setAllowMissing(false)).root();
|
||||
this.configuration = new HOCONConfigWrapper(conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigurationComments getComments() {
|
||||
return this.comments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigInitializer<HOCONFileConfigProvider> getInitializer() {
|
||||
return this.initializer;
|
||||
}
|
||||
|
||||
public String serializeValue(@NotNull String key, @NotNull Object value) {
|
||||
// 带有 key=value 的新空对象
|
||||
return ConfigFactory.empty()
|
||||
.withValue(key, ConfigValueFactory.fromAnyRef(value))
|
||||
.root().render();
|
||||
}
|
||||
|
||||
public @NotNull Set<String> getKeys() {
|
||||
return getKeys(null, true);
|
||||
}
|
||||
|
||||
@Contract("null,_->!null")
|
||||
public @Nullable Set<String> getKeys(@Nullable String sectionKey, boolean deep) {
|
||||
if (sectionKey == null) { // 当前路径
|
||||
return HOCONUtils.getKeysFromObject(this.configuration, deep, "");
|
||||
}
|
||||
|
||||
HOCONConfigWrapper section;
|
||||
try {
|
||||
// 获取目标字段所在路径
|
||||
section = (HOCONConfigWrapper) this.configuration.get(sectionKey);
|
||||
} catch (ClassCastException e) {
|
||||
// 值和类型不匹配
|
||||
throw new HOCONGetValueException(e);
|
||||
}
|
||||
if (section == null) {
|
||||
return null;
|
||||
}
|
||||
return HOCONUtils.getKeysFromObject(section, deep, "");
|
||||
}
|
||||
|
||||
public @Nullable Object getValue(@NotNull String key) {
|
||||
return this.configuration.get(key);
|
||||
}
|
||||
|
||||
public @Nullable List<String> getHeaderComments(@Nullable String key) {
|
||||
return this.comments.getHeaderComment(key);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package cc.carm.lib.configuration.hocon.exception;
|
||||
|
||||
public class HOCONGetValueException extends RuntimeException {
|
||||
public HOCONGetValueException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public HOCONGetValueException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public HOCONGetValueException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public HOCONGetValueException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package cc.carm.lib.configuration.hocon.util;
|
||||
|
||||
import cc.carm.lib.configuration.hocon.HOCONConfigWrapper;
|
||||
import cc.carm.lib.configuration.hocon.exception.HOCONGetValueException;
|
||||
import com.typesafe.config.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class HOCONUtils {
|
||||
|
||||
private HOCONUtils() {
|
||||
}
|
||||
|
||||
public static String getSimplePath(String path, char separator) {
|
||||
int index = path.lastIndexOf(separator);
|
||||
return (index == -1) ? path : path.substring(index + 1);
|
||||
}
|
||||
|
||||
public static HOCONConfigWrapper getObjectOn(@NotNull HOCONConfigWrapper parent, @NotNull String path, char separator) {
|
||||
String currentPath = path;
|
||||
HOCONConfigWrapper currentObject = parent;
|
||||
int index;
|
||||
while ((index = currentPath.indexOf(separator)) != -1) {
|
||||
HOCONConfigWrapper previousObject = currentObject;
|
||||
String pathName = currentPath.substring(0, index);
|
||||
try {
|
||||
currentObject = (HOCONConfigWrapper) previousObject.getDirect(pathName);
|
||||
} catch (ClassCastException e) {
|
||||
throw new HOCONGetValueException(e);
|
||||
}
|
||||
if (currentObject == null) {
|
||||
currentObject = new HOCONConfigWrapper(ConfigFactory.empty().root());
|
||||
previousObject.setDirect(pathName, currentObject);
|
||||
}
|
||||
currentPath = currentPath.substring(index + 1);
|
||||
}
|
||||
|
||||
return currentObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 Object 中获取所有键
|
||||
* 思路:在第一次执行时 prefix 应该是 ""
|
||||
* 后续找到了更深层的键,将会变为 "deep."
|
||||
* 下一次键名就是 "deep.key"
|
||||
*
|
||||
* @param parent Object
|
||||
* @param deep 是否更深层获取
|
||||
* @param prefix 当前 Object 键名前缀
|
||||
* @return Object 中的所有键
|
||||
*/
|
||||
public static Set<String> getKeysFromObject(HOCONConfigWrapper parent, boolean deep, String prefix) {
|
||||
return parent.getSource().entrySet().stream().collect(
|
||||
LinkedHashSet::new,
|
||||
(set, entry) -> {
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof HOCONConfigWrapper && deep) {
|
||||
set.addAll(HOCONUtils.getKeysFromObject((HOCONConfigWrapper) value, true, prefix + entry.getKey() + "."));
|
||||
} else {
|
||||
set.add(prefix + entry.getKey());
|
||||
}
|
||||
},
|
||||
LinkedHashSet::addAll
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Object 保存为字符串
|
||||
* 并使用注释器打上注释
|
||||
*
|
||||
* @param object Object
|
||||
* @param commenter 注释器
|
||||
* @return 保存的字符串
|
||||
*/
|
||||
public static @NotNull String renderWithComment(@NotNull HOCONConfigWrapper object, @NotNull Function<String, List<String>> commenter) {
|
||||
return HOCONUtils.makeConfigWithComment(object, "", commenter).root().render(
|
||||
ConfigRenderOptions.defaults()
|
||||
.setJson(false)
|
||||
.setOriginComments(false)
|
||||
);
|
||||
}
|
||||
|
||||
public static @NotNull Config makeConfigWithComment(@NotNull HOCONConfigWrapper object, @NotNull String prefix, @NotNull Function<String, List<String>> commenter) {
|
||||
Config config = ConfigFactory.empty();
|
||||
for (Map.Entry<String, Object> entry : object.getSource().entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String fullKey = prefix + key;
|
||||
Object value = entry.getValue();
|
||||
ConfigValue result;
|
||||
if (value instanceof Iterable) {
|
||||
result = ConfigValueFactory.fromIterable((Iterable<?>) value);
|
||||
} else if (value instanceof HOCONConfigWrapper) {
|
||||
result = makeConfigWithComment((HOCONConfigWrapper) value, fullKey + ".", commenter).root();
|
||||
} else {
|
||||
result = ConfigValueFactory.fromAnyRef(value);
|
||||
}
|
||||
result = result.withOrigin(
|
||||
ConfigOriginFactory.newSimple()
|
||||
.withComments(commenter.apply(fullKey))
|
||||
);
|
||||
config = config.withValue(key, result);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package online.flowerinsnow.test.easyconfiguration;
|
||||
|
||||
import cc.carm.lib.configuration.EasyConfiguration;
|
||||
import cc.carm.lib.configuration.hocon.HOCONFileConfigProvider;
|
||||
import online.flowerinsnow.test.easyconfiguration.config.SimpleConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class HOCONTest {
|
||||
@Test
|
||||
public void onTest() {
|
||||
HOCONFileConfigProvider provider = EasyConfiguration.from(new File("target/hocon.conf"));
|
||||
provider.initialize(SimpleConfig.class);
|
||||
// provider.initialize(DatabaseConfiguration.class);
|
||||
|
||||
System.out.println(SimpleConfig.TEST_INT.getNotNull());
|
||||
SimpleConfig.TEST_INT.set((int) (Math.random() * 100 * 5));
|
||||
System.out.println(SimpleConfig.TEST_INT.getNotNull());
|
||||
|
||||
try {
|
||||
provider.save();
|
||||
provider.reload();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package online.flowerinsnow.test.easyconfiguration.config;
|
||||
|
||||
import cc.carm.lib.configuration.core.Configuration;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
|
||||
public interface SimpleConfig extends Configuration {
|
||||
@HeaderComment("测试字段 int")
|
||||
ConfiguredValue<Integer> TEST_INT = ConfiguredValue.of(Integer.class, 15);
|
||||
|
||||
@HeaderComment("测试字段 List<String>")
|
||||
ConfiguredList<String> TEST_LIST_STRING = ConfiguredList.of(String.class, "li", "li", "li1");
|
||||
|
||||
@HeaderComment("测试对象")
|
||||
interface TestObject extends Configuration {
|
||||
|
||||
@HeaderComment("测试字段 Boolean")
|
||||
ConfiguredValue<Boolean> TEST_BOOLEAN = ConfiguredValue.of(Boolean.class, true);
|
||||
|
||||
@HeaderComment("inner")
|
||||
interface InnerObject extends Configuration {
|
||||
@HeaderComment("测试字段")
|
||||
public static final ConfiguredValue<Boolean> TEST_BOOLEAN_1 = ConfiguredValue.of(Boolean.class, true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>3.9.0</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
<artifactId>easyconfiguration-json</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-demo</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
+9
-7
@@ -1,14 +1,17 @@
|
||||
package cc.carm.lib.configuration;
|
||||
|
||||
import cc.carm.lib.configuration.bungee.BungeeConfigProvider;
|
||||
import cc.carm.lib.configuration.json.JSONConfigProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class EasyConfiguration {
|
||||
|
||||
public static BungeeConfigProvider from(File file, String source) {
|
||||
BungeeConfigProvider provider = new BungeeConfigProvider(file);
|
||||
private EasyConfiguration() {
|
||||
}
|
||||
|
||||
public static JSONConfigProvider from(File file, String source) {
|
||||
JSONConfigProvider provider = new JSONConfigProvider(file);
|
||||
try {
|
||||
provider.initializeFile(source);
|
||||
provider.initializeConfig();
|
||||
@@ -18,17 +21,16 @@ public class EasyConfiguration {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public static BungeeConfigProvider from(File file) {
|
||||
public static JSONConfigProvider from(File file) {
|
||||
return from(file, file.getName());
|
||||
}
|
||||
|
||||
public static BungeeConfigProvider from(String fileName) {
|
||||
public static JSONConfigProvider from(String fileName) {
|
||||
return from(fileName, fileName);
|
||||
}
|
||||
|
||||
public static BungeeConfigProvider from(String fileName, String source) {
|
||||
public static JSONConfigProvider from(String fileName, String source) {
|
||||
return from(new File(fileName), source);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package cc.carm.lib.configuration.json;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
/**
|
||||
* Some code comes from BungeeCord's implementation of the JsonConfiguration.
|
||||
*
|
||||
* @author md_5, CarmJos
|
||||
*/
|
||||
public class JSONConfigProvider extends FileConfigProvider<JSONConfigWrapper> {
|
||||
|
||||
protected final Gson gson = new GsonBuilder()
|
||||
.serializeNulls().disableHtmlEscaping().setPrettyPrinting()
|
||||
.registerTypeAdapter(
|
||||
JSONConfigWrapper.class,
|
||||
(JsonSerializer<JSONConfigWrapper>) (src, typeOfSrc, context) -> context.serialize(src.data)
|
||||
).create();
|
||||
|
||||
protected JSONConfigWrapper configuration;
|
||||
protected ConfigInitializer<JSONConfigProvider> initializer;
|
||||
|
||||
public JSONConfigProvider(@NotNull File file) {
|
||||
super(file);
|
||||
this.initializer = new ConfigInitializer<>(this);
|
||||
}
|
||||
|
||||
public void initializeConfig() {
|
||||
onReload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull JSONConfigWrapper getConfiguration() {
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReload() {
|
||||
LinkedHashMap<?, ?> map = null;
|
||||
|
||||
try (FileInputStream is = new FileInputStream(file)) {
|
||||
map = gson.fromJson(new InputStreamReader(is, StandardCharsets.UTF_8), LinkedHashMap.class);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (map == null) map = new LinkedHashMap<>();
|
||||
|
||||
this.configuration = new JSONConfigWrapper(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigurationComments getComments() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws Exception {
|
||||
try (Writer writer = new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8)) {
|
||||
gson.toJson(configuration.data, writer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigInitializer<? extends ConfigurationProvider<JSONConfigWrapper>> getInitializer() {
|
||||
return this.initializer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package cc.carm.lib.configuration.json;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Some code comes from BungeeCord's implementation of the JsonConfiguration.
|
||||
*
|
||||
* @author md_5, CarmJos
|
||||
*/
|
||||
public class JSONConfigWrapper implements ConfigurationWrapper<Map<String, Object>> {
|
||||
|
||||
private static final char SEPARATOR = '.';
|
||||
protected final Map<String, Object> data;
|
||||
|
||||
JSONConfigWrapper(Map<?, ?> map) {
|
||||
this.data = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
|
||||
|
||||
if (entry.getValue() instanceof Map) {
|
||||
this.data.put(key, new JSONConfigWrapper((Map<?, ?>) entry.getValue()));
|
||||
} else {
|
||||
this.data.put(key, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getSource() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<String> getKeys(boolean deep) {
|
||||
return getValues(deep).keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
if (deep) {
|
||||
Map<String, Object> values = new LinkedHashMap<>();
|
||||
mapChildrenValues(values, this, null, true);
|
||||
return values;
|
||||
} else return new LinkedHashMap<>(this.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
if (value instanceof Map) {
|
||||
value = new JSONConfigWrapper((Map<?, ?>) value);
|
||||
}
|
||||
|
||||
JSONConfigWrapper section = getSectionFor(path);
|
||||
if (section == this) {
|
||||
if (value == null) {
|
||||
this.data.remove(path);
|
||||
} else {
|
||||
this.data.put(path, value);
|
||||
}
|
||||
} else {
|
||||
section.set(getChild(path), value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path) {
|
||||
return get(path) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
JSONConfigWrapper section = getSectionFor(path);
|
||||
return section == this ? data.get(path) : section.get(getChild(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList(@NotNull String path) {
|
||||
return get(path) instanceof List<?>;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<?> getList(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof List<?>) ? (List<?>) val : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurationSection(@NotNull String path) {
|
||||
return get(path) instanceof JSONConfigWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable JSONConfigWrapper getConfigurationSection(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof JSONConfigWrapper) ? (JSONConfigWrapper) val : null;
|
||||
}
|
||||
|
||||
private JSONConfigWrapper getSectionFor(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
if (index == -1) return this;
|
||||
|
||||
String root = path.substring(0, index);
|
||||
Object section = this.data.get(root);
|
||||
if (section == null) {
|
||||
section = new JSONConfigWrapper(new LinkedHashMap<>());
|
||||
this.data.put(root, section);
|
||||
}
|
||||
|
||||
return (JSONConfigWrapper) section;
|
||||
}
|
||||
|
||||
private String getChild(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
return (index == -1) ? path : path.substring(index + 1);
|
||||
}
|
||||
|
||||
|
||||
protected void mapChildrenValues(@NotNull Map<String, Object> output, @NotNull JSONConfigWrapper section,
|
||||
@Nullable String parent, boolean deep) {
|
||||
for (Map.Entry<String, Object> entry : section.data.entrySet()) {
|
||||
String path = (parent == null ? "" : parent + ".") + entry.getKey();
|
||||
output.remove(path);
|
||||
output.put(path, entry.getValue());
|
||||
if (deep && entry.getValue() instanceof JSONConfigWrapper) {
|
||||
this.mapChildrenValues(output, (JSONConfigWrapper) entry.getValue(), path, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package config;
|
||||
|
||||
import cc.carm.lib.configuration.EasyConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||
import cc.carm.lib.configuration.json.JSONConfigProvider;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JSONConfigTest {
|
||||
|
||||
protected final JSONConfigProvider provider = EasyConfiguration.from("target/config.json", "config.json");
|
||||
|
||||
|
||||
@Test
|
||||
public void onTest() {
|
||||
|
||||
ConfigurationTest.testDemo(this.provider);
|
||||
ConfigurationTest.testInner(this.provider);
|
||||
|
||||
System.out.println("----------------------------------------------------");
|
||||
provider.getConfiguration().getValues(true).forEach((k, v) -> System.out.println(k + ": " + v));
|
||||
System.out.println("----------------------------------------------------");
|
||||
provider.getConfiguration().getValues(false).forEach((k, v) -> System.out.println(k + ": " + v));
|
||||
System.out.println("----------------------------------------------------");
|
||||
|
||||
ConfigurationTest.save(this.provider);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>3.9.0</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
|
||||
<log4j.version>2.22.0</log4j.version>
|
||||
</properties>
|
||||
<artifactId>easyconfiguration-sql</artifactId>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.10.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>easysql-beecp</artifactId>
|
||||
<version>0.4.7</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<version>2.1.210</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-demo</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,26 @@
|
||||
package cc.carm.lib.configuration;
|
||||
|
||||
import cc.carm.lib.configuration.sql.SQLConfigProvider;
|
||||
import cc.carm.lib.easysql.api.SQLManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EasyConfiguration {
|
||||
|
||||
private EasyConfiguration() {
|
||||
}
|
||||
|
||||
public static SQLConfigProvider from(@NotNull SQLManager sqlManager, @NotNull String tableName, @NotNull String namespace) {
|
||||
SQLConfigProvider provider = new SQLConfigProvider(sqlManager, tableName, namespace);
|
||||
try {
|
||||
provider.initializeConfig();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
public static SQLConfigProvider from(@NotNull SQLManager sqlManager, @NotNull String tableName) {
|
||||
return from(sqlManager, tableName, "base");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package cc.carm.lib.configuration.sql;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.easysql.api.SQLManager;
|
||||
import cc.carm.lib.easysql.api.SQLQuery;
|
||||
import cc.carm.lib.easysql.api.SQLTable;
|
||||
import cc.carm.lib.easysql.api.enums.IndexType;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.util.*;
|
||||
|
||||
public class SQLConfigProvider extends ConfigurationProvider<SQLConfigWrapper> {
|
||||
|
||||
public static Gson GSON = new GsonBuilder().serializeNulls().disableHtmlEscaping().setPrettyPrinting().create();
|
||||
|
||||
protected final @NotNull SQLManager sqlManager;
|
||||
protected final @NotNull SQLTable table;
|
||||
protected final @NotNull String namespace;
|
||||
|
||||
protected ConfigInitializer<SQLConfigProvider> initializer;
|
||||
protected ConfigurationComments comments = new ConfigurationComments();
|
||||
protected SQLConfigWrapper rootConfiguration;
|
||||
|
||||
protected final @NotNull Set<String> updated = new HashSet<>();
|
||||
|
||||
public SQLConfigProvider(@NotNull SQLManager sqlManager, @NotNull String tableName, @NotNull String namespace) {
|
||||
this.sqlManager = sqlManager;
|
||||
this.table = SQLTable.of(tableName, builder -> {
|
||||
|
||||
builder.addColumn("namespace", "VARCHAR(32) NOT NULL");
|
||||
builder.addColumn("path", "VARCHAR(96) NOT NULL");
|
||||
|
||||
builder.addColumn("type", "TINYINT NOT NULL DEFAULT 0");
|
||||
builder.addColumn("value", "TEXT");
|
||||
|
||||
builder.addColumn("inline_comment", "TEXT");
|
||||
builder.addColumn("header_comments", "MEDIUMTEXT");
|
||||
|
||||
builder.addColumn("create_time", "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
builder.addColumn("update_time", "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP");
|
||||
|
||||
builder.setIndex(
|
||||
IndexType.PRIMARY_KEY, "pk_" + tableName.toLowerCase(),
|
||||
"namespace", "path"
|
||||
);
|
||||
builder.setTableSettings("ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
|
||||
});
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull SQLConfigWrapper getConfiguration() {
|
||||
return rootConfiguration;
|
||||
}
|
||||
|
||||
public void initializeConfig() throws Exception {
|
||||
this.table.create(this.sqlManager);
|
||||
this.initializer = new ConfigInitializer<>(this);
|
||||
onReload();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReload() throws Exception {
|
||||
this.comments = new ConfigurationComments();
|
||||
LinkedHashMap<String, Object> values = new LinkedHashMap<>();
|
||||
|
||||
try (SQLQuery query = this.table.createQuery()
|
||||
.addCondition("namespace", namespace)
|
||||
.build().execute()) {
|
||||
ResultSet rs = query.getResultSet();
|
||||
while (rs.next()) {
|
||||
String path = rs.getString("path");
|
||||
int type = rs.getInt("type");
|
||||
try {
|
||||
SQLValueResolver<?> resolver = SQLValueTypes.get(type);
|
||||
if (resolver == null) throw new IllegalStateException("No resolver for type #" + type);
|
||||
String value = rs.getString("value");
|
||||
values.put(path, resolver.resolve(value));
|
||||
|
||||
loadInlineComment(path, rs.getString("inline_comment"));
|
||||
loadHeaderComment(path, rs.getString("header_comments"));
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.rootConfiguration = new SQLConfigWrapper(this, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws Exception {
|
||||
if (this.updated.isEmpty()) return;
|
||||
|
||||
Date date = new Date();
|
||||
List<Object[]> values = new ArrayList<>();
|
||||
List<String> deletes = new ArrayList<>();
|
||||
|
||||
for (String path : this.updated) {
|
||||
Object value = this.rootConfiguration.get(path);
|
||||
if (value == null) {
|
||||
deletes.add(path);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (value instanceof SQLConfigWrapper) {
|
||||
value = getSourceMap(((SQLConfigWrapper) value).getSource());
|
||||
}
|
||||
|
||||
SQLValueResolver<?> type = SQLValueTypes.get(value.getClass());
|
||||
if (type != null) {
|
||||
values.add(new Object[]{
|
||||
this.namespace, path, date,
|
||||
type.getID(), type.serializeObject(value),
|
||||
getComments().getInlineComment(path),
|
||||
GSON.toJson(getComments().getHeaderComment(path))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.updated.clear();
|
||||
|
||||
this.table.createReplaceBatch()
|
||||
.setColumnNames("namespace", "path", "update_time", "type", "value", "inline_comment", "header_comments")
|
||||
.setAllParams(values)
|
||||
.execute();
|
||||
|
||||
for (String path : deletes) {
|
||||
this.table.createDelete()
|
||||
.addCondition("namespace", this.namespace)
|
||||
.addCondition("path", path)
|
||||
.build().execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigurationComments getComments() {
|
||||
return this.comments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigInitializer<? extends ConfigurationProvider<SQLConfigWrapper>> getInitializer() {
|
||||
return this.initializer;
|
||||
}
|
||||
|
||||
|
||||
protected void loadInlineComment(String path, String comment) {
|
||||
if (comment == null) return;
|
||||
comment = comment.trim();
|
||||
if (comment.isEmpty()) return;
|
||||
|
||||
this.comments.setInlineComment(path, comment);
|
||||
}
|
||||
|
||||
protected void loadHeaderComment(String path, String commentJson) {
|
||||
if (commentJson == null) return;
|
||||
commentJson = commentJson.trim();
|
||||
if (commentJson.isEmpty()) return;
|
||||
|
||||
List<String> headerComments = GSON.fromJson(commentJson, new TypeToken<List<String>>() {
|
||||
}.getType());
|
||||
if (headerComments == null || headerComments.isEmpty()) return;
|
||||
|
||||
this.comments.setHeaderComments(path, headerComments);
|
||||
}
|
||||
|
||||
protected static Map<String, Object> getSourceMap(Map<String, Object> map) {
|
||||
Map<String, Object> source = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
if (entry.getValue() instanceof SQLConfigWrapper) {
|
||||
source.put(entry.getKey(), getSourceMap(((SQLConfigWrapper) entry.getValue()).getSource()));
|
||||
} else {
|
||||
source.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package cc.carm.lib.configuration.sql;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* For SQL configs, that primary path will be directly mapped to the value.
|
||||
*
|
||||
* @author CarmJos
|
||||
*/
|
||||
public class SQLConfigWrapper implements ConfigurationWrapper<Map<String, Object>> {
|
||||
|
||||
private static final char SEPARATOR = '.';
|
||||
protected final @NotNull SQLConfigProvider provider;
|
||||
protected final @NotNull Map<String, Object> data;
|
||||
|
||||
SQLConfigWrapper(@NotNull SQLConfigProvider provider, @NotNull Map<?, ?> map) {
|
||||
this.provider = provider;
|
||||
this.data = new LinkedHashMap<>();
|
||||
|
||||
for (Map.Entry<?, ?> entry : map.entrySet()) {
|
||||
String key = (entry.getKey() == null) ? "null" : entry.getKey().toString();
|
||||
|
||||
if (entry.getValue() instanceof Map) {
|
||||
this.data.put(key, new SQLConfigWrapper(provider, (Map<?, ?>) entry.getValue()));
|
||||
} else {
|
||||
this.data.put(key, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getSource() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<String> getKeys(boolean deep) {
|
||||
return getValues(deep).keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
// Deep is not supported for SQL configs.
|
||||
return new LinkedHashMap<>(this.data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
if (value instanceof Map) {
|
||||
value = new SQLConfigWrapper(this.provider, (Map<?, ?>) value);
|
||||
}
|
||||
|
||||
SQLConfigWrapper section = getSectionFor(path);
|
||||
if (section == this) {
|
||||
if (value == null) {
|
||||
this.data.remove(path);
|
||||
} else {
|
||||
this.data.put(path, value);
|
||||
}
|
||||
} else {
|
||||
section.set(getChild(path), value);
|
||||
}
|
||||
|
||||
this.provider.updated.add(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path) {
|
||||
return get(path) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
SQLConfigWrapper section = getSectionFor(path);
|
||||
return section == this ? data.get(path) : section.get(getChild(path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList(@NotNull String path) {
|
||||
return get(path) instanceof List<?>;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<?> getList(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof List<?>) ? (List<?>) val : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurationSection(@NotNull String path) {
|
||||
return get(path) instanceof SQLConfigWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SQLConfigWrapper getConfigurationSection(@NotNull String path) {
|
||||
Object val = get(path);
|
||||
return (val instanceof SQLConfigWrapper) ? (SQLConfigWrapper) val : null;
|
||||
}
|
||||
|
||||
private SQLConfigWrapper getSectionFor(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
if (index == -1) return this;
|
||||
|
||||
String root = path.substring(0, index);
|
||||
Object section = this.data.computeIfAbsent(root, k -> new SQLConfigWrapper(this.provider, new LinkedHashMap<>()));
|
||||
|
||||
return (SQLConfigWrapper) section;
|
||||
}
|
||||
|
||||
private String getChild(String path) {
|
||||
int index = path.indexOf(SEPARATOR);
|
||||
return (index == -1) ? path : path.substring(index + 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package cc.carm.lib.configuration.sql;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SQLValueResolver<T> {
|
||||
|
||||
public static <V> SQLValueResolver<V> of(int id, @NotNull Class<V> clazz,
|
||||
@NotNull ConfigDataFunction<String, V> resolver) {
|
||||
return of(id, clazz, resolver, String::valueOf);
|
||||
}
|
||||
|
||||
public static <V> SQLValueResolver<V> of(int id, @NotNull Class<V> clazz,
|
||||
@NotNull ConfigDataFunction<String, V> resolver,
|
||||
@NotNull Function<V, String> serializer) {
|
||||
return new SQLValueResolver<>(id, clazz, resolver, serializer);
|
||||
}
|
||||
|
||||
protected final int id;
|
||||
protected final @NotNull Class<T> clazz;
|
||||
protected final @NotNull ConfigDataFunction<String, T> resolver;
|
||||
protected final @NotNull Function<T, String> serializer;
|
||||
|
||||
protected SQLValueResolver(int id, @NotNull Class<T> clazz,
|
||||
@NotNull ConfigDataFunction<String, T> resolver,
|
||||
@NotNull Function<T, String> serializer) {
|
||||
this.id = id;
|
||||
this.clazz = clazz;
|
||||
this.resolver = resolver;
|
||||
this.serializer = serializer;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public @NotNull Class<T> getClazz() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public boolean isTypeOf(@NotNull Class<?> clazz) {
|
||||
return this.clazz.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
public @NotNull T resolve(String data) throws Exception {
|
||||
return resolver.parse(data);
|
||||
}
|
||||
|
||||
public @Nullable String serialize(T value) {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
|
||||
public @Nullable String serializeObject(Object value) {
|
||||
return isTypeOf(value.getClass()) ? serialize(clazz.cast(value)) : null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package cc.carm.lib.configuration.sql;
|
||||
|
||||
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
interface SQLValueTypes {
|
||||
|
||||
SQLValueResolver<String> STRING = SQLValueResolver.of(0, String.class, ConfigDataFunction.identity());
|
||||
SQLValueResolver<Byte> BYTE = SQLValueResolver.of(1, Byte.class, Byte::parseByte);
|
||||
SQLValueResolver<Short> SHORT = SQLValueResolver.of(2, Short.class, Short::parseShort);
|
||||
SQLValueResolver<Integer> INTEGER = SQLValueResolver.of(3, Integer.class, Integer::parseInt);
|
||||
SQLValueResolver<Long> LONG = SQLValueResolver.of(4, Long.class, Long::parseLong);
|
||||
SQLValueResolver<Float> FLOAT = SQLValueResolver.of(5, Float.class, Float::parseFloat);
|
||||
SQLValueResolver<Double> DOUBLE = SQLValueResolver.of(6, Double.class, Double::parseDouble);
|
||||
SQLValueResolver<Boolean> BOOLEAN = SQLValueResolver.of(7, Boolean.class, Boolean::parseBoolean);
|
||||
SQLValueResolver<Character> CHAR = SQLValueResolver.of(8, Character.class, s -> s.charAt(0));
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
SQLValueResolver<List> LIST = SQLValueResolver.of(10, List.class,
|
||||
array -> SQLConfigProvider.GSON.fromJson(array, List.class),
|
||||
list -> SQLConfigProvider.GSON.toJson(list)
|
||||
);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
SQLValueResolver<Map> SECTION = SQLValueResolver.of(20, Map.class,
|
||||
section -> SQLConfigProvider.GSON.fromJson(section, LinkedHashMap.class),
|
||||
section -> SQLConfigProvider.GSON.toJson(section)
|
||||
);
|
||||
|
||||
static @NotNull SQLValueResolver<?>[] values() {
|
||||
return new SQLValueResolver<?>[]{
|
||||
STRING, BYTE, SHORT, INTEGER, LONG, FLOAT, DOUBLE, BOOLEAN, CHAR, LIST, SECTION
|
||||
};
|
||||
}
|
||||
|
||||
static SQLValueResolver<?> valueOf(int index) {
|
||||
return values()[index];
|
||||
}
|
||||
|
||||
static @Nullable SQLValueResolver<?> get(int id) {
|
||||
return Arrays.stream(values()).filter(v -> v.id == id).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
static @Nullable SQLValueResolver<?> get(Class<?> typeClazz) {
|
||||
return Arrays.stream(values()).filter(v -> v.isTypeOf(typeClazz)).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package config;
|
||||
|
||||
import cc.carm.lib.configuration.EasyConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||
import cc.carm.lib.configuration.sql.SQLConfigProvider;
|
||||
import cc.carm.lib.easysql.EasySQL;
|
||||
import cc.carm.lib.easysql.api.SQLManager;
|
||||
import cc.carm.lib.easysql.beecp.BeeDataSourceConfig;
|
||||
import org.junit.Test;
|
||||
|
||||
public class SQLConfigTest {
|
||||
|
||||
@Test
|
||||
public void onTest() {
|
||||
// test();
|
||||
}
|
||||
|
||||
|
||||
public void test() {
|
||||
BeeDataSourceConfig config = new BeeDataSourceConfig();
|
||||
config.setDriverClassName("org.h2.Driver");
|
||||
config.setJdbcUrl("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=MySQL;");
|
||||
SQLManager manager = EasySQL.createManager(config);
|
||||
manager.setDebugMode(true);
|
||||
|
||||
SQLConfigProvider provider = EasyConfiguration.from(manager, "conf_test", "TESTING");
|
||||
|
||||
ConfigurationTest.testDemo(provider);
|
||||
ConfigurationTest.testInner(provider);
|
||||
|
||||
System.out.println("----------------------------------------------------");
|
||||
provider.getConfiguration().getValues(true).forEach((k, v) -> System.out.println(k + ": " + v));
|
||||
System.out.println("----------------------------------------------------");
|
||||
provider.getConfiguration().getValues(false).forEach((k, v) -> System.out.println(k + ": " + v));
|
||||
System.out.println("----------------------------------------------------");
|
||||
|
||||
ConfigurationTest.save(provider);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="WARN" packages="config.SQLConfigTest">
|
||||
<Appenders>
|
||||
<console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
|
||||
</console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="info">
|
||||
<filters>
|
||||
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
<RegexFilter regex=".*\$\{[^}]*\}.*" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
</filters>
|
||||
<AppenderRef ref="File"/>
|
||||
<appender-ref ref="Console"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
CREATE TABLE IF NOT EXISTS conf
|
||||
(
|
||||
`namespace` VARCHAR(32) NOT NULL, # 命名空间 (代表其属于谁,类似于单个配置文件地址的概念)
|
||||
`path` VARCHAR(96) NOT NULL, # 配置路径 (ConfigPath)
|
||||
`type` TINYINT UNSIGNED NOT NULL DEFAULT 0, # 数据类型 (Integer/Byte/List/Map/...)
|
||||
`value` MEDIUMTEXT, # 配置项的值 (可能为JSON格式)
|
||||
`inline_comments` TEXT, # 行内注释
|
||||
`header_comments` MEDIUMTEXT, # 顶部注释
|
||||
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, # 创建时间
|
||||
`update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`namespace`, `path`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4;
|
||||
|
||||
+21
-5
@@ -5,13 +5,15 @@
|
||||
<parent>
|
||||
<artifactId>easyconfiguration-parent</artifactId>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<version>1.0.6</version>
|
||||
<version>3.9.0</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<properties>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
</properties>
|
||||
|
||||
<artifactId>easyconfiguration-yaml</artifactId>
|
||||
@@ -28,8 +30,22 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>cc.carm.lib</groupId>
|
||||
<artifactId>yamlconfiguration-commented</artifactId>
|
||||
<version>2.0.2</version>
|
||||
<artifactId>yamlcommentwriter</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.parent.groupId}</groupId>
|
||||
<artifactId>easyconfiguration-demo</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bspfsystems</groupId>
|
||||
<artifactId>yamlconfiguration</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package cc.carm.lib.configuration;
|
||||
|
||||
import cc.carm.lib.configuration.yaml.YamlConfigProvider;
|
||||
import cc.carm.lib.configuration.yaml.YAMLConfigProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class EasyConfiguration {
|
||||
|
||||
public static YamlConfigProvider from(File file, String source) {
|
||||
YamlConfigProvider provider = new YamlConfigProvider(file);
|
||||
private EasyConfiguration() {
|
||||
}
|
||||
|
||||
public static YAMLConfigProvider from(File file, String source) {
|
||||
YAMLConfigProvider provider = new YAMLConfigProvider(file);
|
||||
try {
|
||||
provider.initializeFile(source);
|
||||
provider.initializeConfig();
|
||||
@@ -18,17 +21,16 @@ public class EasyConfiguration {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public static YamlConfigProvider from(File file) {
|
||||
public static YAMLConfigProvider from(File file) {
|
||||
return from(file, file.getName());
|
||||
}
|
||||
|
||||
public static YamlConfigProvider from(String fileName) {
|
||||
public static YAMLConfigProvider from(String fileName) {
|
||||
return from(fileName, fileName);
|
||||
}
|
||||
|
||||
public static YamlConfigProvider from(String fileName, String source) {
|
||||
public static YAMLConfigProvider from(String fileName, String source) {
|
||||
return from(new File(fileName), source);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package cc.carm.lib.configuration.yaml;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigInitializer;
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationComments;
|
||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||
import cc.carm.lib.yamlcommentupdater.CommentedYAML;
|
||||
import cc.carm.lib.yamlcommentupdater.CommentedYAMLWriter;
|
||||
import org.bspfsystems.yamlconfiguration.configuration.ConfigurationSection;
|
||||
import org.bspfsystems.yamlconfiguration.file.FileConfiguration;
|
||||
import org.bspfsystems.yamlconfiguration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class YAMLConfigProvider extends FileConfigProvider<YAMLSectionWrapper> implements CommentedYAML {
|
||||
|
||||
protected final @NotNull ConfigurationComments comments = new ConfigurationComments();
|
||||
protected YamlConfiguration configuration;
|
||||
protected ConfigInitializer<YAMLConfigProvider> initializer;
|
||||
|
||||
public YAMLConfigProvider(@NotNull File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public void initializeConfig() {
|
||||
this.configuration = YamlConfiguration.loadConfiguration(file);
|
||||
this.initializer = new ConfigInitializer<>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull YAMLSectionWrapper getConfiguration() {
|
||||
return YAMLSectionWrapper.of(configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onReload() throws Exception {
|
||||
configuration.load(getFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigurationComments getComments() {
|
||||
return this.comments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws Exception {
|
||||
try {
|
||||
CommentedYAMLWriter.writeWithComments(this, this.file);
|
||||
} catch (Exception ex) {
|
||||
configuration.save(file);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigInitializer<YAMLConfigProvider> getInitializer() {
|
||||
return this.initializer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String serializeValue(@NotNull String key, @NotNull Object value) {
|
||||
FileConfiguration temp = new YamlConfiguration();
|
||||
temp.set(key, value);
|
||||
return temp.saveToString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getKeys(@Nullable String sectionKey, boolean deep) {
|
||||
if (sectionKey == null) return configuration.getKeys(deep);
|
||||
|
||||
ConfigurationSection section = configuration.getConfigurationSection(sectionKey);
|
||||
if (section == null) return null;
|
||||
|
||||
return section.getKeys(deep);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object getValue(@NotNull String key) {
|
||||
return configuration.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> getHeaderComments(@Nullable String key) {
|
||||
return comments.getHeaderComment(key);
|
||||
}
|
||||
|
||||
}
|
||||
+23
-5
@@ -2,6 +2,7 @@ package cc.carm.lib.configuration.yaml;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import org.bspfsystems.yamlconfiguration.configuration.ConfigurationSection;
|
||||
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -11,17 +12,22 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class YamlSectionWrapper implements ConfigurationWrapper {
|
||||
public class YAMLSectionWrapper implements ConfigurationWrapper<ConfigurationSection> {
|
||||
|
||||
private final ConfigurationSection section;
|
||||
|
||||
private YamlSectionWrapper(ConfigurationSection section) {
|
||||
private YAMLSectionWrapper(ConfigurationSection section) {
|
||||
this.section = section;
|
||||
}
|
||||
|
||||
@Contract("!null->!null")
|
||||
public static @Nullable YamlSectionWrapper of(@Nullable ConfigurationSection section) {
|
||||
return section == null ? null : new YamlSectionWrapper(section);
|
||||
public static @Nullable YAMLSectionWrapper of(@Nullable ConfigurationSection section) {
|
||||
return section == null ? null : new YAMLSectionWrapper(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigurationSection getSource() {
|
||||
return this.section;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,7 +71,19 @@ public class YamlSectionWrapper implements ConfigurationWrapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigurationWrapper getConfigurationSection(@NotNull String path) {
|
||||
public @Nullable YAMLSectionWrapper getConfigurationSection(@NotNull String path) {
|
||||
return of(this.section.getConfigurationSection(path));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends ConfigurationSerializable> T getSerializable(@NotNull String path, @NotNull Class<T> clazz) {
|
||||
return getSerializable(path, clazz, null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Contract("_, _, !null -> !null")
|
||||
public <T extends ConfigurationSerializable> T getSerializable(@NotNull String path, @NotNull Class<T> clazz, @Nullable T defaultValue) {
|
||||
return this.section.getSerializable(path, clazz, defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package cc.carm.lib.configuration.yaml;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
|
||||
import cc.carm.lib.configuration.yaml.builder.YAMLConfigBuilder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class YAMLValue<T> extends CachedConfigValue<T> {
|
||||
|
||||
public static @NotNull YAMLConfigBuilder builder() {
|
||||
return new YAMLConfigBuilder();
|
||||
}
|
||||
|
||||
public YAMLValue(@NotNull ValueManifest<T> manifest) {
|
||||
super(manifest);
|
||||
}
|
||||
|
||||
public YAMLConfigProvider getYAMLProvider() {
|
||||
ConfigurationProvider<?> provider = getProvider();
|
||||
if (provider instanceof YAMLConfigProvider) return (YAMLConfigProvider) getProvider();
|
||||
else throw new IllegalStateException("Provider is not a YamlConfigProvider");
|
||||
}
|
||||
|
||||
public YAMLSectionWrapper getYAMLConfig() {
|
||||
return getYAMLProvider().getConfiguration();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package cc.carm.lib.configuration.yaml;
|
||||
|
||||
import org.bspfsystems.yamlconfiguration.commented.CommentsProvider;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class YamlComments implements CommentsProvider {
|
||||
|
||||
Map<String, String[]> comments = new HashMap<>();
|
||||
|
||||
protected Map<String, String[]> getComments() {
|
||||
return comments;
|
||||
}
|
||||
|
||||
public void set(@NotNull String path, @NotNull String... comments) {
|
||||
if (comments.length == 0) {
|
||||
getComments().remove(path);
|
||||
} else {
|
||||
getComments().put(path, comments);
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable String[] get(@NotNull String path) {
|
||||
return getComments().get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] apply(String s) {
|
||||
return get(s);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package cc.carm.lib.configuration.yaml;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||
import org.bspfsystems.yamlconfiguration.commented.CommentedYamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class YamlConfigProvider extends FileConfigProvider {
|
||||
|
||||
YamlComments comments = new YamlComments();
|
||||
CommentedYamlConfiguration configuration;
|
||||
|
||||
public YamlConfigProvider(@NotNull File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public void initializeConfig() {
|
||||
this.configuration = CommentedYamlConfiguration.loadConfiguration(comments, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigurationWrapper getConfiguration() {
|
||||
return YamlSectionWrapper.of(configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() throws Exception {
|
||||
configuration.load(getFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws Exception {
|
||||
configuration.save(getFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComments(@NotNull String path, @NotNull String... comments) {
|
||||
this.comments.set(path, comments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String[] getComments(@NotNull String path) {
|
||||
return this.comments.get(path);
|
||||
}
|
||||
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package cc.carm.lib.configuration.yaml.builder;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
|
||||
import cc.carm.lib.configuration.yaml.YAMLConfigProvider;
|
||||
|
||||
public abstract class AbstractYAMLBuilder<T, B extends AbstractYAMLBuilder<T, B>>
|
||||
extends AbstractConfigBuilder<T, B, YAMLConfigProvider> {
|
||||
|
||||
public AbstractYAMLBuilder() {
|
||||
super(YAMLConfigProvider.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cc.carm.lib.configuration.yaml.builder;
|
||||
|
||||
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
|
||||
import cc.carm.lib.configuration.yaml.builder.serializable.SerializableBuilder;
|
||||
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class YAMLConfigBuilder extends ConfigBuilder {
|
||||
|
||||
public <V extends ConfigurationSerializable> @NotNull SerializableBuilder<V> ofSerializable(@NotNull Class<V> valueClass) {
|
||||
return new SerializableBuilder<>(valueClass);
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package cc.carm.lib.configuration.yaml.builder.serializable;
|
||||
|
||||
import cc.carm.lib.configuration.yaml.builder.AbstractYAMLBuilder;
|
||||
import cc.carm.lib.configuration.yaml.value.ConfiguredSerializable;
|
||||
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SerializableBuilder<T extends ConfigurationSerializable>
|
||||
extends AbstractYAMLBuilder<T, SerializableBuilder<T>> {
|
||||
|
||||
protected final @NotNull Class<T> valueClass;
|
||||
|
||||
public SerializableBuilder(@NotNull Class<T> valueClass) {
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull SerializableBuilder<T> getThis() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfiguredSerializable<T> build() {
|
||||
return new ConfiguredSerializable<>(buildManifest(), this.valueClass);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
package cc.carm.lib.configuration.yaml.value;
|
||||
|
||||
import cc.carm.lib.configuration.core.value.ValueManifest;
|
||||
import cc.carm.lib.configuration.yaml.YAMLValue;
|
||||
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ConfiguredSerializable<T extends ConfigurationSerializable> extends YAMLValue<T> {
|
||||
|
||||
public static <V extends ConfigurationSerializable> ConfiguredSerializable<V> of(@NotNull Class<V> valueClass) {
|
||||
return of(valueClass, null);
|
||||
}
|
||||
|
||||
public static <V extends ConfigurationSerializable> ConfiguredSerializable<V> of(@NotNull Class<V> valueClass,
|
||||
@Nullable V defaultValue) {
|
||||
return builder().ofSerializable(valueClass).defaults(defaultValue).build();
|
||||
}
|
||||
|
||||
protected final @NotNull Class<T> valueClass;
|
||||
|
||||
public ConfiguredSerializable(@NotNull ValueManifest<T> manifest, @NotNull Class<T> valueClass) {
|
||||
super(manifest);
|
||||
this.valueClass = valueClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable T get() {
|
||||
if (!isExpired()) return getCachedOrDefault();
|
||||
|
||||
try {
|
||||
// 若未出现错误,则直接更新缓存并返回。
|
||||
return updateCache(getYAMLConfig().getSerializable(getConfigPath(), valueClass, getDefaultValue()));
|
||||
} catch (Exception e) {
|
||||
// 出现了解析错误,提示并返回默认值。
|
||||
e.printStackTrace();
|
||||
return getDefaultValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@Nullable T value) {
|
||||
updateCache(value);
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package config;
|
||||
|
||||
import cc.carm.lib.configuration.EasyConfiguration;
|
||||
import cc.carm.lib.configuration.yaml.YamlConfigProvider;
|
||||
import config.misc.TestUser;
|
||||
import config.source.DemoConfiguration;
|
||||
import config.source.ComplexConfiguration;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class ConfigTester {
|
||||
|
||||
@Test
|
||||
public void onTest() {
|
||||
|
||||
YamlConfigProvider provider = EasyConfiguration.from("target/config.yml", "config.yml");
|
||||
provider.initialize(DemoConfiguration.class);
|
||||
provider.initialize(ComplexConfiguration.class);
|
||||
|
||||
System.out.println("before: " + ComplexConfiguration.Sub.UUID_CONFIG_VALUE.get());
|
||||
ComplexConfiguration.Sub.UUID_CONFIG_VALUE.set(UUID.randomUUID());
|
||||
System.out.println("after: " + ComplexConfiguration.Sub.UUID_CONFIG_VALUE.get());
|
||||
|
||||
|
||||
ComplexConfiguration.Sub.That.Operators.getNotNull().forEach(System.out::println);
|
||||
List<UUID> operators = IntStream.range(0, 5).mapToObj(i -> UUID.randomUUID()).collect(Collectors.toList());
|
||||
ComplexConfiguration.Sub.That.Operators.set(operators);
|
||||
|
||||
System.out.println(ComplexConfiguration.USER.get());
|
||||
TestUser b = new TestUser(UUID.randomUUID().toString().substring(0, 3), UUID.randomUUID());
|
||||
ComplexConfiguration.USER.set(b);
|
||||
|
||||
ComplexConfiguration.USERS.getNotNull().forEach((k, v) -> System.out.println(k + ": " + v));
|
||||
LinkedHashMap<Integer, UUID> data = new LinkedHashMap<>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
data.put((int) (1000 * Math.random()), UUID.randomUUID());
|
||||
}
|
||||
ComplexConfiguration.USERS.set(data);
|
||||
|
||||
try {
|
||||
provider.save();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package config;
|
||||
|
||||
import cc.carm.lib.configuration.EasyConfiguration;
|
||||
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
|
||||
import cc.carm.lib.configuration.demo.tests.model.AbstractModel;
|
||||
import cc.carm.lib.configuration.yaml.YAMLConfigProvider;
|
||||
import config.model.AnyModel;
|
||||
import config.model.SomeModel;
|
||||
import config.source.ModelConfiguration;
|
||||
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerialization;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class DemoConfigTest {
|
||||
|
||||
static {
|
||||
ConfigurationSerialization.registerClass(SomeModel.class);
|
||||
ConfigurationSerialization.registerClass(AnyModel.class);
|
||||
}
|
||||
|
||||
protected final YAMLConfigProvider provider = EasyConfiguration.from("target/config.yml", "test/test2/config.yml");
|
||||
|
||||
@Test
|
||||
public void onTest() {
|
||||
|
||||
ConfigurationTest.testDemo(this.provider);
|
||||
ConfigurationTest.testInner(this.provider);
|
||||
|
||||
testSerialization(this.provider);
|
||||
|
||||
ConfigurationTest.save(this.provider);
|
||||
}
|
||||
|
||||
|
||||
public static void testSerialization(YAMLConfigProvider provider) {
|
||||
provider.initialize(ModelConfiguration.class);
|
||||
System.out.println("----------------------------------------------------");
|
||||
|
||||
AbstractModel someModel = ModelConfiguration.SOME_MODEL.get();
|
||||
if (someModel != null) System.out.println(someModel.getName());
|
||||
|
||||
AbstractModel anyModel = ModelConfiguration.ANY_MODEL.get();
|
||||
if (anyModel != null) System.out.println(anyModel.getName());
|
||||
|
||||
ModelConfiguration.MODELS.forEach(System.out::println);
|
||||
ModelConfiguration.MODEL_MAP.forEach((v, anyModel1) -> System.out.println(v + " -> " + anyModel1.toString()));
|
||||
|
||||
|
||||
System.out.println("----------------------------------------------------");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package config.model;
|
||||
|
||||
import cc.carm.lib.configuration.demo.tests.model.AbstractModel;
|
||||
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
|
||||
import org.bspfsystems.yamlconfiguration.serialization.SerializableAs;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@SerializableAs("AnyModel")
|
||||
public class AnyModel extends AbstractModel implements ConfigurationSerializable {
|
||||
|
||||
public final boolean bool;
|
||||
|
||||
public AnyModel(@NotNull String name, boolean bool) {
|
||||
super(name);
|
||||
this.bool = bool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AnyModel{" +
|
||||
"name='" + name + '\'' +
|
||||
", bool=" + bool +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> serialize() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("name", name);
|
||||
map.put("state", bool);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static AnyModel random() {
|
||||
return new AnyModel(UUID.randomUUID().toString().substring(0, 5), Math.random() > 0.5);
|
||||
}
|
||||
|
||||
|
||||
@TestOnly
|
||||
public static AnyModel deserialize(Map<String, ?> args) {
|
||||
return new AnyModel((String) args.get("name"), (Boolean) args.get("state"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package config.model;
|
||||
|
||||
import cc.carm.lib.configuration.demo.tests.model.AbstractModel;
|
||||
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
|
||||
import org.bspfsystems.yamlconfiguration.serialization.SerializableAs;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@SerializableAs("SomeModel")
|
||||
public class SomeModel extends AbstractModel implements ConfigurationSerializable {
|
||||
|
||||
public final int num;
|
||||
|
||||
public SomeModel(@NotNull String name, int num) {
|
||||
super(name);
|
||||
this.num = num;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SomeModel{" +
|
||||
"name='" + name + '\'' +
|
||||
", num=" + num +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> serialize() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("name", name);
|
||||
map.put("num", num);
|
||||
return map;
|
||||
}
|
||||
|
||||
public static SomeModel random() {
|
||||
return new SomeModel(UUID.randomUUID().toString().substring(0, 5), (int) (Math.random() * 1000));
|
||||
}
|
||||
|
||||
|
||||
@TestOnly
|
||||
public static SomeModel deserialize(Map<String, ?> args) {
|
||||
return new SomeModel((String) args.get("name"), (Integer) args.get("num"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package config.source;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigComment;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
|
||||
import config.misc.TestUser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ComplexConfiguration extends ConfigurationRoot {
|
||||
|
||||
@ConfigComment({"User测试"})
|
||||
public static final ConfigValue<TestUser> USER = ConfiguredSection
|
||||
.builder(TestUser.class)
|
||||
.defaults(new TestUser("Carm", UUID.randomUUID()))
|
||||
.parseValue((section, defaultValue) -> TestUser.deserialize(section))
|
||||
.serializeValue(TestUser::serialize).build();
|
||||
|
||||
@ConfigComment({"[ID-UUID] 对照表", "", "用于测试Map类型的解析与序列化保存"})
|
||||
public static final ConfigValue<Map<Integer, UUID>> USERS = ConfiguredMap
|
||||
.builder(Integer.class, UUID.class).fromString()
|
||||
.parseKey(Integer::parseInt)
|
||||
.parseValue(v -> Objects.requireNonNull(UUID.fromString(v)))
|
||||
.build();
|
||||
|
||||
public static class Sub {
|
||||
|
||||
@ConfigPath(value = "uuid", root = true)
|
||||
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
|
||||
.builder(UUID.class).fromString()
|
||||
.parseValue((data, defaultValue) -> UUID.fromString(data))
|
||||
.build();
|
||||
|
||||
@ConfigPath("nothing")
|
||||
public static class That {
|
||||
|
||||
public static final ConfigValue<List<UUID>> Operators = ConfiguredList
|
||||
.builder(UUID.class).fromString()
|
||||
.parseValue(s -> Objects.requireNonNull(UUID.fromString(s)))
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package config.source;
|
||||
|
||||
import cc.carm.lib.configuration.core.ConfigurationRoot;
|
||||
import cc.carm.lib.configuration.core.annotation.ConfigPath;
|
||||
import cc.carm.lib.configuration.core.annotation.HeaderComment;
|
||||
import cc.carm.lib.configuration.core.value.ConfigValue;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
|
||||
import cc.carm.lib.configuration.core.value.type.ConfiguredSectionMap;
|
||||
import cc.carm.lib.configuration.demo.tests.model.AbstractModel;
|
||||
import cc.carm.lib.configuration.yaml.value.ConfiguredSerializable;
|
||||
import config.model.AnyModel;
|
||||
import config.model.SomeModel;
|
||||
|
||||
@HeaderComment("以下内容用于测试序列化")
|
||||
@ConfigPath("model-test")
|
||||
public class ModelConfiguration extends ConfigurationRoot {
|
||||
|
||||
public static final ConfigValue<? extends AbstractModel> SOME_MODEL = ConfiguredSerializable.of(
|
||||
SomeModel.class, SomeModel.random()
|
||||
);
|
||||
|
||||
public static final ConfigValue<? extends AbstractModel> ANY_MODEL = ConfiguredSerializable.of(
|
||||
AnyModel.class, AnyModel.random()
|
||||
);
|
||||
|
||||
public static final ConfiguredList<AnyModel> MODELS = ConfiguredList.builderOf(AnyModel.class)
|
||||
.fromMap()
|
||||
.parseValue(AnyModel::deserialize).serializeValue(AnyModel::serialize)
|
||||
.defaults(AnyModel.random(), AnyModel.random(), AnyModel.random())
|
||||
.build();
|
||||
|
||||
public static final ConfiguredSectionMap<String, AnyModel> MODEL_MAP = ConfiguredMap.builderOf(String.class, AnyModel.class)
|
||||
.asLinkedMap().fromSection()
|
||||
.parseValue(v -> new AnyModel(v.getString("name", "EMPTY"), v.getBoolean("state", false)))
|
||||
.serializeValue(AnyModel::serialize)
|
||||
.defaults(m -> {
|
||||
m.put("a", AnyModel.random());
|
||||
m.put("b", AnyModel.random());
|
||||
})
|
||||
.build();
|
||||
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
# Test Header
|
||||
version: 1.0
|
||||
@@ -0,0 +1,2 @@
|
||||
version: 1.0
|
||||
test-save: false
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
package cc.carm.lib.configuration.bungee;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BungeeConfigProvider extends FileConfigProvider {
|
||||
|
||||
Configuration configuration;
|
||||
|
||||
public BungeeConfigProvider(@NotNull File file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public void initializeConfig() throws IOException {
|
||||
this.configuration = getLoader().load(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ConfigurationWrapper getConfiguration() {
|
||||
return BungeeSectionWrapper.of(configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() throws Exception {
|
||||
this.configuration = getLoader().load(file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() throws Exception {
|
||||
getLoader().save(configuration, file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setComments(@NotNull String path, @NotNull String... comments) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String[] getComments(@NotNull String path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static ConfigurationProvider getLoader() {
|
||||
return ConfigurationProvider.getProvider(YamlConfiguration.class);
|
||||
}
|
||||
|
||||
}
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
package cc.carm.lib.configuration.bungee;
|
||||
|
||||
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BungeeSectionWrapper implements ConfigurationWrapper {
|
||||
|
||||
private final Configuration section;
|
||||
|
||||
private BungeeSectionWrapper(Configuration section) {
|
||||
this.section = section;
|
||||
}
|
||||
|
||||
@Contract("!null->!null")
|
||||
public static @Nullable BungeeSectionWrapper of(@Nullable Configuration section) {
|
||||
return section == null ? null : new BungeeSectionWrapper(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Set<String> getKeys(boolean deep) {
|
||||
return new LinkedHashSet<>(section.getKeys());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Map<String, Object> getValues(boolean deep) {
|
||||
return section.getKeys().stream()
|
||||
.collect(Collectors.toMap(key -> key, section::get, (a, b) -> b, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(@NotNull String path, @Nullable Object value) {
|
||||
this.section.set(path, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(@NotNull String path) {
|
||||
return this.section.contains(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object get(@NotNull String path) {
|
||||
return this.section.get(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isList(@NotNull String path) {
|
||||
return get(path) instanceof List<?>;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<?> getList(@NotNull String path) {
|
||||
return this.section.getList(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConfigurationSection(@NotNull String path) {
|
||||
return true; // No provided functions :( SRY
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ConfigurationWrapper getConfigurationSection(@NotNull String path) {
|
||||
return of(this.section.getSection(path));
|
||||
}
|
||||
}
|
||||
-1513
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user