1
mirror of https://github.com/CarmJos/EasyConfiguration.git synced 2026-06-05 02:58:20 +08:00

Compare commits

...

30 Commits

Author SHA1 Message Date
carm 505b86ec9c feat(sql): Support sql based configuration provider. 2023-12-24 04:52:56 +08:00
carm 374f646f9e feat(sql): Support sql based configuration provider. 2023-12-24 04:52:28 +08:00
carm aa50345329 feat(interface): Support interface root configurations. 2023-12-23 22:25:16 +08:00
carm db8b227317 feat(interface): Support interface root configurations. 2023-12-23 22:24:34 +08:00
carm 60eed8a14d docs(readme): Add English version of README files. 2023-12-23 21:24:45 +08:00
carm d41630be72 docs(readme): Add English version of README files. 2023-12-23 21:24:15 +08:00
carm 1c3a2b01ea docs(readme): Add English version of README files. 2023-12-23 21:18:50 +08:00
carm 1ede74c692 docs(readme): Add English version of README files. 2023-12-23 21:18:20 +08:00
carm 67456a8aac docs(readme): Add English version of README files. 2023-12-23 21:17:43 +08:00
carm f422900fb5 docs(readme): Add English version of README files. 2023-12-23 21:16:00 +08:00
carm b3e0007eba docs(readme): Add English version of README files. 2023-12-23 21:15:15 +08:00
carm 89c8ec8adf fix(type): 修复部分原生类型无法转换的问题。#54 2023-12-21 20:47:13 +08:00
huanmeng_qwq e8f49bf8bc fix(core): 解决 #54 的问题 (#55) 2023-12-21 20:43:15 +08:00
dependabot[bot] 8ac980fdbb chore(deps): Bump org.apache.maven.plugins:maven-compiler-plugin (#53)
Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.11.0 to 3.12.0.
- [Release notes](https://github.com/apache/maven-compiler-plugin/releases)
- [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.11.0...maven-compiler-plugin-3.12.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-compiler-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 22:51:35 +08:00
dependabot[bot] 9f2e4bc0cb chore(deps): Bump org.apache.maven.plugins:maven-surefire-plugin (#52)
Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.2.2 to 3.2.3.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.2.2...surefire-3.2.3)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-15 18:16:48 +08:00
dependabot[bot] ae16d131c3 chore(deps): Bump org.apache.maven.plugins:maven-javadoc-plugin (#51)
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.6.2 to 3.6.3.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.6.2...maven-javadoc-plugin-3.6.3)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-javadoc-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-05 08:24:39 +08:00
dependabot[bot] bd8470a6e8 chore(deps): Bump org.jetbrains:annotations from 24.0.1 to 24.1.0 (#50)
Bumps [org.jetbrains:annotations](https://github.com/JetBrains/java-annotations) from 24.0.1 to 24.1.0.
- [Release notes](https://github.com/JetBrains/java-annotations/releases)
- [Changelog](https://github.com/JetBrains/java-annotations/blob/master/CHANGELOG.md)
- [Commits](https://github.com/JetBrains/java-annotations/compare/24.0.1...24.1.0)

---
updated-dependencies:
- dependency-name: org.jetbrains:annotations
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-16 23:32:53 +08:00
dependabot[bot] 6af2d3fef5 chore(deps): Bump org.apache.maven.plugins:maven-javadoc-plugin (#48)
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.6.0 to 3.6.2.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.6.0...maven-javadoc-plugin-3.6.2)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-javadoc-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-09 14:17:49 +08:00
dependabot[bot] 00f83002c5 chore(deps): Bump org.apache.maven.plugins:maven-surefire-plugin (#49)
Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.2.1...surefire-3.2.2)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-09 14:17:24 +08:00
dependabot[bot] 0aa548cbbf chore(deps): Bump org.bspfsystems:yamlconfiguration from 1.4.0 to 2.0.0 (#47)
Bumps [org.bspfsystems:yamlconfiguration](https://github.com/bspfsystems/YamlConfiguration) from 1.4.0 to 2.0.0.
- [Release notes](https://github.com/bspfsystems/YamlConfiguration/releases)
- [Commits](https://github.com/bspfsystems/YamlConfiguration/compare/v1.4.0...v2.0.0)

---
updated-dependencies:
- dependency-name: org.bspfsystems:yamlconfiguration
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-02 15:23:03 +08:00
dependabot[bot] 9c0ed1c5c2 chore(deps): Bump org.apache.maven.plugins:maven-surefire-plugin (#46)
Bumps [org.apache.maven.plugins:maven-surefire-plugin](https://github.com/apache/maven-surefire) from 3.1.2 to 3.2.1.
- [Release notes](https://github.com/apache/maven-surefire/releases)
- [Commits](https://github.com/apache/maven-surefire/compare/surefire-3.1.2...surefire-3.2.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-surefire-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-25 22:49:50 +08:00
dependabot[bot] bb0998cbac chore(deps): Bump com.typesafe:config from 1.4.2 to 1.4.3 (#45)
Bumps [com.typesafe:config](https://github.com/lightbend/config) from 1.4.2 to 1.4.3.
- [Release notes](https://github.com/lightbend/config/releases)
- [Changelog](https://github.com/lightbend/config/blob/main/NEWS.md)
- [Commits](https://github.com/lightbend/config/compare/v1.4.2...v1.4.3)

---
updated-dependencies:
- dependency-name: com.typesafe:config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-17 23:40:40 +08:00
dependabot[bot] 1eb16f00b6 chore(deps): Bump org.bspfsystems:yamlconfiguration from 1.3.3 to 1.4.0 (#44)
Bumps [org.bspfsystems:yamlconfiguration](https://github.com/bspfsystems/YamlConfiguration) from 1.3.3 to 1.4.0.
- [Release notes](https://github.com/bspfsystems/YamlConfiguration/releases)
- [Commits](https://github.com/bspfsystems/YamlConfiguration/compare/v1.3.3...v1.4.0)

---
updated-dependencies:
- dependency-name: org.bspfsystems:yamlconfiguration
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-14 15:19:19 +08:00
dependabot[bot] 5ccf63b423 chore(deps): Bump org.apache.maven.plugins:maven-shade-plugin (#43)
Bumps [org.apache.maven.plugins:maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.5.0...maven-shade-plugin-3.5.1)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-shade-plugin
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-26 12:48:04 +08:00
dependabot[bot] f1c0c74574 chore(deps): Bump org.apache.maven.plugins:maven-javadoc-plugin (#42)
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.5.0 to 3.6.0.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.5.0...maven-javadoc-plugin-3.6.0)

---
updated-dependencies:
- dependency-name: org.apache.maven.plugins:maven-javadoc-plugin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-19 12:16:10 +08:00
carm 8ee074474c fix(init): 添加文件初始化测试 #40 2023-08-29 02:39:38 +08:00
carm 27a68ead7c feat(map): 支持通过ConfigurationSection创建MapValue。 2023-08-28 17:00:56 +08:00
carm c6cce5208f fix(init): 尝试修复初始化创建无用文件的问题 #40 2023-08-10 02:00:46 +08:00
carm bc67de06f6 fix(init): 尝试修复初始化创建无用文件的问题 #40 2023-08-10 01:57:15 +08:00
carm b668794f5d feat(parse): 新增两种忽略默认值的parseValue方法。 2023-08-07 22:06:03 +08:00
49 changed files with 1340 additions and 450 deletions
+16
View File
@@ -0,0 +1,16 @@
# 欢迎使用 EasyConfiguration
这个项目刚刚创建,详细的Javadoc与开发指南还在补充,请给我一点时间~
## 基本定义
Value: 实际配置的单例值。
Manifest: 用于描述值基本配置的对象。
Provider: 用于提供配置文件的接口。
Wrapper: 用于包装配置文件的接口。
Initializer: 用于初始化的接口
-3
View File
@@ -1,3 +0,0 @@
# 欢迎使用 EasyConfiguration
这个项目刚刚创建,详细的Javadoc与开发指南还在补充,请给我一点时间~
+1 -1
View File
@@ -41,7 +41,7 @@ jobs:
rm -rf docs rm -rf docs
mkdir -vp docs mkdir -vp docs
cp -vrf core/target/apidocs/* 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" - name: "Generate the Javadoc sitemap"
id: sitemap id: sitemap
+49 -57
View File
@@ -1,10 +1,5 @@
```text
____ _____ ____ __ _ README LANGUAGES [ [**English**](README.md) | [中文](README_CN.md) ]
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
/___/ /___/
```
# EasyConfiguration # EasyConfiguration
@@ -15,56 +10,55 @@
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/EasyConfiguration) ![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/EasyConfiguration)
![](https://visitor-badge.glitch.me/badge?page_id=EasyConfiguration.readme) ![](https://visitor-badge.glitch.me/badge?page_id=EasyConfiguration.readme)
轻松(做)配置,简单便捷的通用配置文件加载、读取与更新工具,可自定义配置格式。 **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
- 支持复杂配置的手动序列化、反序列化。
- 提供多种builder形式,快速构建 `ConfigValue<?>` 对象。
- 支持通过注解规定配置对应的路径、注释等信息。
## 开发 - 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).
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java) 查看部分代码演示,更多演示详见 [开发介绍](.documentation/README.md) 。 For the latest JavaDoc release, [CLICK HERE](https://CarmJos.github.io/EasyConfiguration).
### 依赖方式 ### 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> <details>
<summary>远程库配置</summary> <summary>Remote Repository Configuration</summary>
```xml ```xml
<project> <project>
<repositories> <repositories>
<repository> <repository>
<!--采用Maven中心库,安全稳定,但版本更新需要等待同步--> <!-- Using Maven Central Repository for secure and stable updates, though synchronization might be needed. -->
<id>maven</id> <id>maven</id>
<name>Maven Central</name> <name>Maven Central</name>
<url>https://repo1.maven.org/maven2</url> <url>https://repo1.maven.org/maven2</url>
</repository> </repository>
<repository> <repository>
<!--采用github依赖库,实时更新,但需要配置 (推荐) --> <!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
<id>EasyConfiguration</id> <id>EasyConfiguration</id>
<name>GitHub Packages</name> <name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url> <url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
</repository> </repository>
<repository>
<!--采用我的私人依赖库,简单方便,但可能因为变故而无法使用-->
<id>carm-repo</id>
<name>Carm's Repo</name>
<url>https://repo.carm.cc/repository/maven-public/</url>
</repository>
</repositories> </repositories>
</project> </project>
``` ```
@@ -72,13 +66,12 @@
</details> </details>
<details> <details>
<summary>通用原生依赖</summary> <summary>Generic Native Dependency</summary>
```xml ```xml
<project> <project>
<dependencies> <dependencies>
<!--基础实现部分,需要自行实现“Provider”“Wrapper”--> <!-- Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”. -->
<dependency> <dependency>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-core</artifactId> <artifactId>easyconfiguration-core</artifactId>
@@ -86,7 +79,7 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<!--基于YAML文件的实现版本,可用于全部Java环境。--> <!-- YAML file-based implementation, compatible with all Java environments. -->
<dependency> <dependency>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-yaml</artifactId> <artifactId>easyconfiguration-yaml</artifactId>
@@ -94,8 +87,7 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<!--基于JSON文件的实现版本,可用于全部Java环境。--> <!-- JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments. -->
<!--需要注意的是,JSON不支持文件注释。-->
<dependency> <dependency>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-json</artifactId> <artifactId>easyconfiguration-json</artifactId>
@@ -109,42 +101,39 @@
</details> </details>
#### Gradle 依赖 #### Gradle Dependency
<details> <details>
<summary>远程库配置</summary> <summary>Remote Repository Configuration</summary>
```groovy ```groovy
repositories { repositories {
// 采用Maven中心库,安全稳定,但版本更新需要等待同步 // Using Maven Central Repository for secure and stable updates, though synchronization might be needed.
mavenCentral() mavenCentral()
// 采用github依赖库,实时更新,但需要配置 (推荐) // Using GitHub dependencies for real-time updates, configuration required (recommended).
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' } maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
maven { url 'https://repo.carm.cc/repository/maven-public/' }
} }
``` ```
</details> </details>
<details> <details>
<summary>通用原生依赖</summary> <summary>Generic Native Dependency</summary>
```groovy ```groovy
dependencies { dependencies {
//基础实现部分,需要自行实现“Provider”“Wrapper” // Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”.
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]" 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]" api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
//基于JSON文件的实现版本,可用于全部Java环境。 // JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments.
//需要注意的是,JSON不支持文件注释。
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]" api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
} }
@@ -152,23 +141,26 @@ dependencies {
</details> </details>
## 衍生项目 ## Derived Projects
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos ) ### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos)
EasyConfiguration for MineCraft! EasyConfiguration for MineCraft!
开始在 MineCraft 相关服务器平台上轻松(做)配置吧! Easily manage configurations on MineCraft-related server platforms.
目前支持 BungeeCord, Bukkit(Spigot) 服务端,后续将支持更多平台。 Currently supports BungeeCord, Bukkit (Spigot) servers, with more platforms to be supported soon.
## 支持与捐赠 ## Support and Donation
若您觉得本插件做的不错,您可以通过捐赠支持我! If you appreciate this plugin, consider supporting me with a donation!
感谢您对开源项目的支持! Thank you for supporting open-source projects!
<img height=25% width=25% src="https://raw.githubusercontent.com/CarmJos/CarmJos/main/img/donate-code.jpg" alt=""/> Many thanks to Jetbrains for kindly providing a license for us to work on this and other open-source projects.
## 开源协议 [![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/ArtformGames/ResidenceList)
本项目源码采用 [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html) 开源协议。
## Open Source License
This project's source code is licensed under the [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html).
+180
View File
@@ -0,0 +1,180 @@
```text
____ _____ ____ __ _
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
/___/ /___/
```
README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
# EasyConfiguration
[![version](https://img.shields.io/github/v/release/CarmJos/EasyConfiguration)](https://github.com/CarmJos/EasyConfiguration/releases)
[![License](https://img.shields.io/github/license/CarmJos/EasyConfiguration)](https://www.gnu.org/licenses/lgpl-3.0.html)
[![workflow](https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/carmjos/easyconfiguration/badge)](https://www.codefactor.io/repository/github/carmjos/easyconfiguration)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/EasyConfiguration)
![](https://visitor-badge.glitch.me/badge?page_id=EasyConfiguration.readme)
**轻松(做)配置!**
一款简单便捷的通用配置文件加载、读取与更新工具,可自定义配置的格式。
## 特性 & 优势
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
- 支持复杂配置的手动序列化、反序列化。
- 提供多种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://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/ArtformGames/ResidenceList)
## 开源协议
本项目源码采用 [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html) 开源协议。
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>3.7.1</version> <version>3.9.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
@@ -14,7 +14,7 @@ import java.util.List;
/** /**
* 配置文件类初始化方法 * 配置文件类初始化方法
* 用于初始化 {@link ConfigurationRoot} 中的每个 {@link ConfigValue} 对象 * 用于初始化 {@link Configuration} 中的每个 {@link ConfigValue} 对象
* *
* @param <T> {@link ConfigurationProvider} 配置文件的数据来源 * @param <T> {@link ConfigurationProvider} 配置文件的数据来源
* @author CarmJos * @author CarmJos
@@ -30,21 +30,21 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
/** /**
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。 * 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
* *
* @param clazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 * @param clazz 配置文件类,须继承于 {@link Configuration} 。
* @param saveDefaults 是否写入默认值(默认为 true)。 * @param saveDefaults 是否写入默认值(默认为 true)。
*/ */
public void initialize(@NotNull Class<? extends ConfigurationRoot> clazz, boolean saveDefaults) { public void initialize(@NotNull Class<? extends Configuration> clazz, boolean saveDefaults) {
initialize(clazz, saveDefaults, true); initialize(clazz, saveDefaults, true);
} }
/** /**
* 初始化指定类的所有 {@link ConfigValue} 对象。 * 初始化指定类的所有 {@link ConfigValue} 对象。
* *
* @param clazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 * @param clazz 配置文件类,须继承于 {@link Configuration} 。
* @param saveDefaults 是否写入默认值(默认为 true)。 * @param saveDefaults 是否写入默认值(默认为 true)。
* @param loadSubClasses 是否加载内部子类(默认为 true)。 * @param loadSubClasses 是否加载内部子类(默认为 true)。
*/ */
public void initialize(@NotNull Class<? extends ConfigurationRoot> clazz, public void initialize(@NotNull Class<? extends Configuration> clazz,
boolean saveDefaults, boolean loadSubClasses) { boolean saveDefaults, boolean loadSubClasses) {
initializeStaticClass( initializeStaticClass(
clazz, null, null, clazz, null, null,
@@ -61,21 +61,21 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
} }
/** /**
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。 * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
* *
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。 * @param config 配置文件实例类,须实现 {@link Configuration} 。
*/ */
public void initialize(@NotNull ConfigurationRoot config) { public void initialize(@NotNull Configuration config) {
initialize(config, true); initialize(config, true);
} }
/** /**
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。 * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
* *
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。 * @param config 配置文件实例类,须实现 {@link Configuration} 。
* @param saveDefaults 是否写入默认值(默认为 true)。 * @param saveDefaults 是否写入默认值(默认为 true)。
*/ */
public void initialize(@NotNull ConfigurationRoot config, boolean saveDefaults) { public void initialize(@NotNull Configuration config, boolean saveDefaults) {
initializeInstance( initializeInstance(
config, null, null, config, null, null,
null, null, null, null, null, null,
@@ -92,7 +92,7 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
// 针对实例类的初始化方法 // 针对实例类的初始化方法
private void initializeInstance(@NotNull ConfigurationRoot root, private void initializeInstance(@NotNull Configuration root,
@Nullable String parentPath, @Nullable String fieldName, @Nullable String parentPath, @Nullable String fieldName,
@Nullable ConfigPath fieldPath, @Nullable ConfigPath fieldPath,
@Nullable HeaderComment fieldHeaderComments, @Nullable HeaderComment fieldHeaderComments,
@@ -114,7 +114,7 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
@Nullable HeaderComment fieldHeaderComments, @Nullable HeaderComment fieldHeaderComments,
@Nullable InlineComment fieldInlineComments, @Nullable InlineComment fieldInlineComments,
boolean saveDefaults, boolean loadSubClasses) { boolean saveDefaults, boolean loadSubClasses) {
if (!ConfigurationRoot.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类 if (!Configuration.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类
String path = getClassPath(clazz, parentPath, fieldName, fieldPath); String path = getClassPath(clazz, parentPath, fieldName, fieldPath);
this.provider.setHeaderComment(path, getClassHeaderComments(clazz, fieldHeaderComments)); this.provider.setHeaderComment(path, getClassHeaderComments(clazz, fieldHeaderComments));
if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments)); if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments));
@@ -147,10 +147,10 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
field.getAnnotation(InlineComment.class), field.getAnnotation(InlineComment.class),
saveDefaults saveDefaults
); );
} else if (source instanceof ConfigurationRoot && object instanceof ConfigurationRoot) { } else if (source instanceof Configuration && object instanceof Configuration) {
// 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。 // 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。
initializeInstance( initializeInstance(
(ConfigurationRoot) object, parent, field.getName(), (Configuration) object, parent, field.getName(),
field.getAnnotation(ConfigPath.class), field.getAnnotation(ConfigPath.class),
field.getAnnotation(HeaderComment.class), field.getAnnotation(HeaderComment.class),
field.getAnnotation(InlineComment.class), field.getAnnotation(InlineComment.class),
@@ -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,7 +1,8 @@
package cc.carm.lib.configuration.core; package cc.carm.lib.configuration.core;
/** /**
* 配置文件类的根节点,用于标注该类用于记录配置文件中的配置信息。 * The root node of the configuration file class,
* which is used to label and record the configuration information.
*/ */
public abstract class ConfigurationRoot { public abstract class ConfigurationRoot implements Configuration {
} }
@@ -1,9 +1,10 @@
package cc.carm.lib.configuration.core.builder.list; package cc.carm.lib.configuration.core.builder.list;
import cc.carm.lib.configuration.core.annotation.InlineComment;
import cc.carm.lib.configuration.core.function.ConfigDataFunction; import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.Map;
public class ConfigListBuilder<V> { public class ConfigListBuilder<V> {
@@ -13,7 +14,7 @@ public class ConfigListBuilder<V> {
this.valueClass = valueClass; 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<Object, S> sourceParser,
@NotNull ConfigDataFunction<S, V> valueParser, @NotNull ConfigDataFunction<S, V> valueParser,
@NotNull ConfigDataFunction<V, S> valueSerializer, @NotNull ConfigDataFunction<V, S> valueSerializer,
@@ -45,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()
);
}
} }
@@ -12,7 +12,7 @@ import java.util.List;
public class SourceListBuilder<S, V> extends CommonConfigBuilder<List<V>, SourceListBuilder<S, 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 @NotNull ConfigDataFunction<Object, S> sourceParser;
protected final @NotNull Class<V> valueClass; protected final @NotNull Class<V> valueClass;
@@ -21,7 +21,7 @@ public class SourceListBuilder<S, V> extends CommonConfigBuilder<List<V>, Source
protected @NotNull ConfigDataFunction<V, S> valueSerializer; protected @NotNull ConfigDataFunction<V, S> valueSerializer;
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer; 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 Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
@NotNull ConfigDataFunction<V, S> valueSerializer, @NotNull ConfigDataFunction<V, S> valueSerializer,
@NotNull ConfigDataFunction<S, Object> sourceSerializer) { @NotNull ConfigDataFunction<S, Object> sourceSerializer) {
@@ -48,6 +48,14 @@ public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
return fromString(ConfigDataFunction.castFromString(this.valueClass)); 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) { public SourceMapBuilder<M, Object, K, V> fromObject(@NotNull ConfigDataFunction<Object, V> valueParser) {
return from(Object.class, valueParser, ConfigDataFunction.toObject()); return from(Object.class, valueParser, ConfigDataFunction.toObject());
} }
@@ -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
);
}
}
@@ -32,6 +32,10 @@ public class SectionValueBuilder<V>
return this; return this;
} }
public @NotNull SectionValueBuilder<V> parseValue(ConfigDataFunction<ConfigurationWrapper<?>, V> valueParser) {
return parseValue((section, path) -> valueParser.parse(section));
}
public @NotNull SectionValueBuilder<V> parseValue(ConfigValueParser<ConfigurationWrapper<?>, V> valueParser) { public @NotNull SectionValueBuilder<V> parseValue(ConfigValueParser<ConfigurationWrapper<?>, V> valueParser) {
this.parser = valueParser; this.parser = valueParser;
return this; return this;
@@ -39,6 +39,10 @@ public class SourceValueBuilder<S, V> extends CommonConfigBuilder<V, SourceValue
return this; 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) { public @NotNull SourceValueBuilder<S, V> parseValue(@NotNull ConfigValueParser<S, V> valueParser) {
this.valueParser = valueParser; this.valueParser = valueParser;
return this; return this;
@@ -50,22 +50,21 @@ public interface ConfigValueParser<T, R> {
@Contract(pure = true) @Contract(pure = true)
static <V> @NotNull ConfigValueParser<Object, V> castObject(Class<V> valueClass) { static <V> @NotNull ConfigValueParser<Object, V> castObject(Class<V> valueClass) {
return (input, defaultValue) -> { return (input, defaultValue) -> {
if (Number.class.isAssignableFrom(valueClass)) { if (Number.class.isAssignableFrom(valueClass)) {
if (Long.class.isAssignableFrom(valueClass)) { if (Long.class.isAssignableFrom(valueClass) || long.class.isAssignableFrom(valueClass)) {
input = longValue().parse(input, (Long) defaultValue); input = longValue().parse(input, (Long) defaultValue);
} else if (Integer.class.isAssignableFrom(valueClass)) { } else if (Integer.class.isAssignableFrom(valueClass) || int.class.isAssignableFrom(valueClass)) {
input = intValue().parse(input, (Integer) defaultValue); input = intValue().parse(input, (Integer) defaultValue);
} else if (Float.class.isAssignableFrom(valueClass)) { } else if (Float.class.isAssignableFrom(valueClass) || float.class.isAssignableFrom(valueClass)) {
input = floatValue().parse(input, (Float) defaultValue); input = floatValue().parse(input, (Float) defaultValue);
} else if (Double.class.isAssignableFrom(valueClass)) { } else if (Double.class.isAssignableFrom(valueClass) || double.class.isAssignableFrom(valueClass)) {
input = doubleValue().parse(input, (Double) defaultValue); input = doubleValue().parse(input, (Double) defaultValue);
} else if (Byte.class.isAssignableFrom(valueClass)) { } else if (Byte.class.isAssignableFrom(valueClass) || byte.class.isAssignableFrom(valueClass)) {
input = byteValue().parse(input, (Byte) defaultValue); input = byteValue().parse(input, (Byte) defaultValue);
} else if (Short.class.isAssignableFrom(valueClass)) { } else if (Short.class.isAssignableFrom(valueClass) || short.class.isAssignableFrom(valueClass)) {
input = shortValue().parse(input, (Short) defaultValue); input = shortValue().parse(input, (Short) defaultValue);
} }
} else if (Boolean.class.isAssignableFrom(valueClass)) { } else if (Boolean.class.isAssignableFrom(valueClass) || boolean.class.isAssignableFrom(valueClass)) {
input = booleanValue().parse(input, (Boolean) defaultValue); input = booleanValue().parse(input, (Boolean) defaultValue);
} else if (Enum.class.isAssignableFrom(valueClass) && input instanceof String) { } else if (Enum.class.isAssignableFrom(valueClass) && input instanceof String) {
String enumName = (String) input; String enumName = (String) input;
@@ -1,7 +1,8 @@
package cc.carm.lib.configuration.core.source; package cc.carm.lib.configuration.core.source;
import cc.carm.lib.configuration.core.ConfigInitializer; 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.ConfigValue;
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue; import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -101,49 +102,49 @@ public abstract class ConfigurationProvider<W extends ConfigurationWrapper<?>> {
/** /**
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。 * 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
* *
* @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 * @param configClazz 配置文件类,须继承于 {@link Configuration} 。
*/ */
public void initialize(Class<? extends ConfigurationRoot> configClazz) { public void initialize(Class<? extends Configuration> configClazz) {
initialize(configClazz, true); initialize(configClazz, true);
} }
/** /**
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。 * 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
* *
* @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 * @param configClazz 配置文件类,须继承于 {@link Configuration} 。
* @param saveDefaults 是否写入默认值(默认为 true)。 * @param saveDefaults 是否写入默认值(默认为 true)。
*/ */
public void initialize(Class<? extends ConfigurationRoot> configClazz, boolean saveDefaults) { public void initialize(Class<? extends Configuration> configClazz, boolean saveDefaults) {
this.getInitializer().initialize(configClazz, saveDefaults); this.getInitializer().initialize(configClazz, saveDefaults);
} }
/** /**
* 初始化指定类的所有 {@link ConfigValue} 对象。 * 初始化指定类的所有 {@link ConfigValue} 对象。
* *
* @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。 * @param configClazz 配置文件类,须继承于 {@link Configuration} 。
* @param saveDefaults 是否写入默认值(默认为 true)。 * @param saveDefaults 是否写入默认值(默认为 true)。
* @param loadSubClasses 是否加载内部子类(默认为 true)。 * @param loadSubClasses 是否加载内部子类(默认为 true)。
*/ */
public void initialize(Class<? extends ConfigurationRoot> configClazz, boolean saveDefaults, boolean loadSubClasses) { public void initialize(Class<? extends Configuration> configClazz, boolean saveDefaults, boolean loadSubClasses) {
this.getInitializer().initialize(configClazz, saveDefaults, loadSubClasses); this.getInitializer().initialize(configClazz, saveDefaults, loadSubClasses);
} }
/** /**
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。 * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
* *
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。 * @param config 配置文件实例类,须实现 {@link Configuration} 。
*/ */
public void initialize(@NotNull ConfigurationRoot config) { public void initialize(@NotNull Configuration config) {
this.getInitializer().initialize(config, true); this.getInitializer().initialize(config, true);
} }
/** /**
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。 * 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link Configuration} 对象。
* *
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。 * @param config 配置文件实例类,须实现 {@link Configuration} 。
* @param saveDefaults 是否写入默认值(默认为 true)。 * @param saveDefaults 是否写入默认值(默认为 true)。
*/ */
public void initialize(@NotNull ConfigurationRoot config, boolean saveDefaults) { public void initialize(@NotNull Configuration config, boolean saveDefaults) {
this.getInitializer().initialize(config, saveDefaults); this.getInitializer().initialize(config, saveDefaults);
} }
@@ -137,39 +137,43 @@ interface ConfigurationReader {
return getWrapper().get(path, def, String.class); 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 @Unmodifiable
default @NotNull List<String> getStringList(@NotNull String path) { default @NotNull List<String> getStringList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.castToString()); return getList(path, ConfigValueParser.castToString());
} }
@Unmodifiable @Unmodifiable
default @NotNull List<Integer> getIntegerList(@NotNull String path) { default @NotNull List<Integer> getIntegerList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.intValue()); return getList(path, ConfigValueParser.intValue());
} }
@Unmodifiable @Unmodifiable
default @NotNull List<Long> getLongList(@NotNull String path) { default @NotNull List<Long> getLongList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.longValue()); return getList(path, ConfigValueParser.longValue());
} }
@Unmodifiable @Unmodifiable
default @NotNull List<Double> getDoubleList(@NotNull String path) { default @NotNull List<Double> getDoubleList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.doubleValue()); return getList(path, ConfigValueParser.doubleValue());
} }
@Unmodifiable @Unmodifiable
default @NotNull List<Float> getFloatList(@NotNull String path) { default @NotNull List<Float> getFloatList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.floatValue()); return getList(path, ConfigValueParser.floatValue());
} }
@Unmodifiable @Unmodifiable
default @NotNull List<Byte> getByteList(@NotNull String path) { default @NotNull List<Byte> getByteList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.byteValue()); return getList(path, ConfigValueParser.byteValue());
} }
@Unmodifiable @Unmodifiable
default @NotNull List<Character> getCharList(@NotNull String path) { default @NotNull List<Character> getCharList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.castObject(Character.class)); return getList(path, ConfigValueParser.castObject(Character.class));
} }
@Unmodifiable @Unmodifiable
@@ -49,15 +49,14 @@ public abstract class FileConfigProvider<W extends ConfigurationWrapper<?>> exte
public void saveResource(@NotNull String resourcePath, boolean replace) public void saveResource(@NotNull String resourcePath, boolean replace)
throws IOException, IllegalArgumentException { throws IOException, IllegalArgumentException {
Objects.requireNonNull(resourcePath, "ResourcePath cannot be null"); 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('\\', '/'); resourcePath = resourcePath.replace('\\', '/');
URL url = this.getClass().getClassLoader().getResource(resourcePath); URL url = this.getClass().getClassLoader().getResource(resourcePath);
if (url == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists"); if (url == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists");
int lastIndex = resourcePath.lastIndexOf('/'); File outDir = file.getParentFile();
File outDir = new File(file.getParentFile(), resourcePath.substring(0, Math.max(lastIndex, 0)));
if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir); if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir);
if (!file.exists() || replace) { if (!file.exists() || replace) {
@@ -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();
}
}
@@ -43,8 +43,7 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements Lis
@Override @Override
public @NotNull List<V> get() { public @NotNull List<V> get() {
if (!isExpired()) return getCachedOrDefault(new ArrayList<>()); if (!isExpired()) return getCachedOrDefault(new ArrayList<>());
// Data that is outdated and needs to be parsed again.
// 已过时的数据,需要重新解析一次。
List<V> list = new ArrayList<>(); List<V> list = new ArrayList<>();
List<?> data = getConfiguration().contains(getConfigPath()) ? List<?> data = getConfiguration().contains(getConfigPath()) ?
getConfiguration().getList(getConfigPath()) : null; getConfiguration().getList(getConfigPath()) : null;
@@ -1,40 +1,15 @@
package cc.carm.lib.configuration.core.value.type; package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.builder.map.ConfigMapCreator;
import cc.carm.lib.configuration.core.function.ConfigDataFunction; import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper; import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import cc.carm.lib.configuration.core.value.ValueManifest; import cc.carm.lib.configuration.core.value.ValueManifest;
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue; import cc.carm.lib.configuration.core.value.impl.ConfigValueMap;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements Map<K, V> { public class ConfiguredMap<K, V> extends ConfigValueMap<K, V, Object> {
public static <K, V> @NotNull ConfigMapCreator<K, V> builderOf(@NotNull Class<K> keyClass,
@NotNull Class<V> valueClass) {
return builder().asMap(keyClass, valueClass);
}
protected final @NotNull Supplier<? extends Map<K, V>> supplier;
protected final @NotNull Class<K> keyClass;
protected final @NotNull Class<V> valueClass;
protected final @NotNull ConfigDataFunction<String, K> keyParser;
protected final @NotNull ConfigDataFunction<Object, V> valueParser;
protected final @NotNull ConfigDataFunction<K, String> keySerializer;
protected final @NotNull ConfigDataFunction<V, Object> valueSerializer;
public ConfiguredMap(@NotNull ValueManifest<Map<K, V>> manifest, public ConfiguredMap(@NotNull ValueManifest<Map<K, V>> manifest,
@NotNull Supplier<? extends Map<K, V>> mapObjSupplier, @NotNull Supplier<? extends Map<K, V>> mapObjSupplier,
@@ -42,165 +17,12 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<Object, V> valueParser, @NotNull Class<V> valueClass, @NotNull ConfigDataFunction<Object, V> valueParser,
@NotNull ConfigDataFunction<K, String> keySerializer, @NotNull ConfigDataFunction<K, String> keySerializer,
@NotNull ConfigDataFunction<V, Object> valueSerializer) { @NotNull ConfigDataFunction<V, Object> valueSerializer) {
super(manifest); super(manifest, Object.class, mapObjSupplier, keyClass, keyParser, valueClass, valueParser, keySerializer, valueSerializer);
this.supplier = mapObjSupplier;
this.keyClass = keyClass;
this.valueClass = valueClass;
this.keyParser = keyParser;
this.valueParser = valueParser;
this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
}
public @NotNull Class<K> getKeyClass() {
return keyClass;
}
public @NotNull Class<V> getValueClass() {
return valueClass;
}
public @NotNull ConfigDataFunction<String, K> getKeyParser() {
return keyParser;
}
public @NotNull ConfigDataFunction<Object, V> getValueParser() {
return valueParser;
}
public @NotNull ConfigDataFunction<K, String> getKeySerializer() {
return keySerializer;
}
public @NotNull ConfigDataFunction<V, Object> getValueSerializer() {
return valueSerializer;
} }
@Override @Override
public @NotNull Map<K, V> get() { public Object getSource(ConfigurationWrapper<?> section, String dataKey) {
if (!isExpired()) return getCachedOrDefault(supplier.get()); return section.get(dataKey);
// 已过时的数据,需要重新解析一次。
Map<K, V> map = supplier.get();
ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath());
if (section == null) return getDefaultFirst(map);
Set<String> keys = section.getKeys(false);
if (keys.isEmpty()) return getDefaultFirst(map);
for (String dataKey : keys) {
Object dataVal = section.get(dataKey);
if (dataVal == null) continue;
try {
K key = keyParser.parse(dataKey);
V value = valueParser.parse(dataVal);
map.put(key, value);
} catch (Exception e) {
e.printStackTrace();
}
}
return updateCache(map);
}
@Override
public V get(Object key) {
return get().get(key);
}
@Override
public void set(@Nullable Map<K, V> value) {
updateCache(value);
if (value == null) setValue(null);
else {
Map<String, Object> data = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : value.entrySet()) {
try {
data.put(
keySerializer.parse(entry.getKey()),
valueSerializer.parse(entry.getValue())
);
} catch (Exception e) {
e.printStackTrace();
}
}
setValue(data);
}
}
public <T> @NotNull T modifyValue(Function<Map<K, V>, T> function) {
Map<K, V> m = get();
T result = function.apply(m);
set(m);
return result;
}
public @NotNull Map<K, V> modifyMap(Consumer<Map<K, V>> consumer) {
Map<K, V> m = get();
consumer.accept(m);
set(m);
return m;
}
@Override
public int size() {
return get().size();
}
@Override
public boolean isEmpty() {
return get().isEmpty();
}
@Override
public boolean containsKey(Object key) {
return get().containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return get().containsValue(value);
}
@Nullable
@Override
public V put(K key, V value) {
return modifyValue(m -> m.put(key, value));
}
@Override
public V remove(Object key) {
return modifyValue(m -> m.remove(key));
}
@Override
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
modifyMap(map -> map.putAll(m));
}
@Override
public void clear() {
modifyMap(Map::clear);
}
@NotNull
@Override
public Set<K> keySet() {
return get().keySet();
}
@NotNull
@Override
public Collection<V> values() {
return get().values();
}
@NotNull
@Override
@Unmodifiable
public Set<Entry<K, V>> entrySet() {
return get().entrySet();
} }
} }
@@ -31,37 +31,55 @@ public class ConfiguredSection<V> extends CachedConfigValue<V> {
this.serializer = serializer; this.serializer = serializer;
} }
/**
* @return Value's type class
*/
public @NotNull Class<V> getValueClass() { public @NotNull Class<V> getValueClass() {
return valueClass; return valueClass;
} }
/**
* @return Value's parser, cast value from section.
*/
public @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> getParser() { public @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> getParser() {
return parser; return parser;
} }
/**
* @return Value's serializer, serialize value to section.
*/
public @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> getSerializer() { public @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> getSerializer() {
return serializer; return serializer;
} }
/**
* @return Get the value that parsed from the configuration section.
*/
@Override @Override
public @Nullable V get() { public @Nullable V get() {
if (!isExpired()) return getCachedOrDefault(); if (!isExpired()) return getCachedOrDefault();
// 已过时的数据,需要重新解析一次。 // Data that is outdated and needs to be parsed again.
ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath()); ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath());
if (section == null) return getDefaultValue(); if (section == null) return getDefaultValue();
try { try {
// 若未出现错误,则直接更新缓存并返回。 // If there are no errors, update the cache and return.
return updateCache(this.parser.parse(section, this.defaultValue)); return updateCache(this.parser.parse(section, this.defaultValue));
} catch (Exception e) { } catch (Exception e) {
// 出现了解析错误,提示并返回默认值。 // There was a parsing error, prompted and returned the default value.
e.printStackTrace(); e.printStackTrace();
return getDefaultValue(); 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 @Override
public void set(V value) { public void set(V value) {
updateCache(value); updateCache(value);
@@ -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);
}
}
@@ -14,6 +14,11 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
return builder().asValue(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) { public static <V> ConfiguredValue<V> of(Class<V> valueClass) {
return of(valueClass, null); return of(valueClass, null);
} }
@@ -36,32 +41,51 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
this.serializer = serializer; this.serializer = serializer;
} }
/**
* @return Value's type class
*/
public @NotNull Class<V> getValueClass() { public @NotNull Class<V> getValueClass() {
return valueClass; return valueClass;
} }
/**
* @return Value's parser, cast value from base object.
*/
public @NotNull ConfigValueParser<Object, V> getParser() { public @NotNull ConfigValueParser<Object, V> getParser() {
return parser; return parser;
} }
/**
* @return Value's serializer, serialize value to base object.
*/
public @NotNull ConfigDataFunction<V, Object> getSerializer() {
return serializer;
}
@Override @Override
public V get() { public V get() {
if (!isExpired()) return getCachedOrDefault(); if (!isExpired()) return getCachedOrDefault();
// 已过时的数据,需要重新解析一次。 // Data that is outdated and needs to be parsed again.
Object value = getValue(); Object value = getValue();
if (value == null) return getDefaultValue(); // 获取的值不存在,直接使用默认值。 if (value == null) return getDefaultValue(); // 获取的值不存在,直接使用默认值。
try { try {
// 若未出现错误,则直接更新缓存并返回。 // If there are no errors, update the cache and return.
return updateCache(this.parser.parse(value, this.defaultValue)); return updateCache(this.parser.parse(value, this.defaultValue));
} catch (Exception e) { } catch (Exception e) {
// 出现了解析错误,提示并返回默认值。 // There was a parsing error, prompted and returned the default value.
e.printStackTrace(); e.printStackTrace();
return getDefaultValue(); 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 @Override
public void set(V value) { public void set(V value) {
updateCache(value); updateCache(value);
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>3.7.1</version> <version>3.9.0</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<properties> <properties>
@@ -1,6 +1,7 @@
package cc.carm.lib.configuration.demo.tests.conf; package cc.carm.lib.configuration.demo.tests.conf;
import cc.carm.lib.configuration.core.ConfigInitializer; 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.ConfigurationRoot;
import cc.carm.lib.configuration.core.annotation.ConfigPath; import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.annotation.HeaderComment; import cc.carm.lib.configuration.core.annotation.HeaderComment;
@@ -18,35 +19,33 @@ import java.util.UUID;
@HeaderComment({"此处内容将显示在配置文件的最上方"}) @HeaderComment({"此处内容将显示在配置文件的最上方"})
public class DemoConfiguration extends ConfigurationRoot { public interface DemoConfiguration extends Configuration {
@ConfigPath(root = true) @ConfigPath(root = true)
protected static final ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D); ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
@ConfigPath(root = true) @ConfigPath(root = true)
public static final ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(Long.class, 1000000L); ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(1000000L);
public static final ConfigValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS); ConfigValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS);
// 支持通过 Class<?> 变量标注子配置,一并注册。 // 支持通过 Class<?> 变量标注子配置,一并注册。
// 注意: 若对应类也有注解,则优先使用类的注解。 // 注意: 若对应类也有注解,则优先使用类的注解。
@ConfigPath("other-class-config") //支持通过注解修改子配置的主路径,若不修改则以变量名自动生成。 @ConfigPath("other-class-config") //支持通过注解修改子配置的主路径,若不修改则以变量名自动生成。
@HeaderComment({"", "Something..."}) // 支持给子路径直接打注释 @HeaderComment({"", "Something..."}) // 支持给子路径直接打注释
@InlineComment("InlineComments for class path") @InlineComment("InlineComments for class path")
public static final Class<?> OTHER = OtherConfiguration.class; Class<?> OTHER = OtherConfiguration.class;
@ConfigPath("user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。 @ConfigPath("user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
@HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。 @HeaderComment({"Section类型数据测试"}) // 通过注解给配置添加注释。
@InlineComment("Section数据也支持InlineComment注释") @InlineComment("Section数据也支持InlineComment注释")
public static final ConfigValue<TestModel> MODEL_TEST = ConfiguredSection ConfigValue<TestModel> MODEL_TEST = ConfiguredSection.builderOf(TestModel.class)
.builderOf(TestModel.class)
.defaults(new TestModel("Carm", UUID.randomUUID())) .defaults(new TestModel("Carm", UUID.randomUUID()))
.parseValue((section, defaultValue) -> TestModel.deserialize(section)) .parseValue((section, defaultValue) -> TestModel.deserialize(section))
.serializeValue(TestModel::serialize).build(); .serializeValue(TestModel::serialize).build();
@HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"}) @HeaderComment({"[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"})
public static final ConfiguredMap<Integer, UUID> USERS = ConfiguredMap ConfiguredMap<Integer, UUID> USERS = ConfiguredMap.builderOf(Integer.class, UUID.class)
.builderOf(Integer.class, UUID.class)
.asLinkedMap().fromString() .asLinkedMap().fromString()
.parseKey(Integer::parseInt) .parseKey(Integer::parseInt)
.parseValue(v -> Objects.requireNonNull(UUID.fromString(v))) .parseValue(v -> Objects.requireNonNull(UUID.fromString(v)))
@@ -57,7 +56,7 @@ public class DemoConfiguration extends ConfigurationRoot {
* 支持内部类的直接注册。 * 支持内部类的直接注册。
* 注意,需要使用 {@link ConfigInitializer#initialize(Class, boolean, boolean)} 方法,并设定第三个参数为 true。 * 注意,需要使用 {@link ConfigInitializer#initialize(Class, boolean, boolean)} 方法,并设定第三个参数为 true。
*/ */
public static class Sub extends ConfigurationRoot { class Sub extends ConfigurationRoot {
@ConfigPath(value = "uuid-value", root = true) @ConfigPath(value = "uuid-value", root = true)
@InlineComment("This is an inline comment") @InlineComment("This is an inline comment")
+2 -2
View File
@@ -6,7 +6,7 @@
<parent> <parent>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<version>3.7.1</version> <version>3.9.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<properties> <properties>
@@ -36,7 +36,7 @@
<dependency> <dependency>
<groupId>com.typesafe</groupId> <groupId>com.typesafe</groupId>
<artifactId>config</artifactId> <artifactId>config</artifactId>
<version>1.4.2</version> <version>1.4.3</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -1,10 +1,8 @@
package online.flowerinsnow.test.easyconfiguration; package online.flowerinsnow.test.easyconfiguration;
import cc.carm.lib.configuration.EasyConfiguration; import cc.carm.lib.configuration.EasyConfiguration;
//import cc.carm.lib.configuration.demo.DatabaseConfiguration;
//import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration;
import cc.carm.lib.configuration.hocon.HOCONFileConfigProvider; import cc.carm.lib.configuration.hocon.HOCONFileConfigProvider;
import online.flowerinsnow.test.easyconfiguration.config.Config; import online.flowerinsnow.test.easyconfiguration.config.SimpleConfig;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
@@ -13,9 +11,15 @@ public class HOCONTest {
@Test @Test
public void onTest() { public void onTest() {
HOCONFileConfigProvider provider = EasyConfiguration.from(new File("target/hocon.conf")); HOCONFileConfigProvider provider = EasyConfiguration.from(new File("target/hocon.conf"));
provider.initialize(Config.class); provider.initialize(SimpleConfig.class);
// provider.initialize(DatabaseConfiguration.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 { try {
provider.save();
provider.reload(); provider.reload();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -1,25 +1,28 @@
package online.flowerinsnow.test.easyconfiguration.config; package online.flowerinsnow.test.easyconfiguration.config;
import cc.carm.lib.configuration.core.ConfigurationRoot; import cc.carm.lib.configuration.core.Configuration;
import cc.carm.lib.configuration.core.annotation.HeaderComment; 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.ConfiguredList;
import cc.carm.lib.configuration.core.value.type.ConfiguredValue; import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
public class Config extends ConfigurationRoot { public interface SimpleConfig extends Configuration {
@HeaderComment("测试字段 int") @HeaderComment("测试字段 int")
public static final ConfiguredValue<Integer> TEST_INT = ConfiguredValue.of(Integer.class, 15); ConfiguredValue<Integer> TEST_INT = ConfiguredValue.of(Integer.class, 15);
@HeaderComment("测试字段 List<String>") @HeaderComment("测试字段 List<String>")
public static final ConfiguredList<String> TEST_LIST_STRING = ConfiguredList.of(String.class, "li", "li", "li1"); ConfiguredList<String> TEST_LIST_STRING = ConfiguredList.of(String.class, "li", "li", "li1");
@HeaderComment("测试对象") @HeaderComment("测试对象")
public static class TestObject extends ConfigurationRoot { interface TestObject extends Configuration {
@HeaderComment("测试字段 Boolean") @HeaderComment("测试字段 Boolean")
public static final ConfiguredValue<Boolean> TEST_BOOLEAN = ConfiguredValue.of(Boolean.class, true); ConfiguredValue<Boolean> TEST_BOOLEAN = ConfiguredValue.of(Boolean.class, true);
@HeaderComment("inner") @HeaderComment("inner")
public static class InnerObject extends ConfigurationRoot { interface InnerObject extends Configuration {
@HeaderComment("测试字段") @HeaderComment("测试字段")
public static final ConfiguredValue<Boolean> TEST_BOOLEAN_1 = ConfiguredValue.of(Boolean.class, true); public static final ConfiguredValue<Boolean> TEST_BOOLEAN_1 = ConfiguredValue.of(Boolean.class, true);
} }
} }
} }
+1 -1
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>3.7.1</version> <version>3.9.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -12,6 +12,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.*; import java.io.*;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
/** /**
@@ -37,6 +38,16 @@ public class JSONConfigProvider extends FileConfigProvider<JSONConfigWrapper> {
} }
public void initializeConfig() { public void initializeConfig() {
onReload();
}
@Override
public @NotNull JSONConfigWrapper getConfiguration() {
return this.configuration;
}
@Override
protected void onReload() {
LinkedHashMap<?, ?> map = null; LinkedHashMap<?, ?> map = null;
try (FileInputStream is = new FileInputStream(file)) { try (FileInputStream is = new FileInputStream(file)) {
@@ -50,17 +61,6 @@ public class JSONConfigProvider extends FileConfigProvider<JSONConfigWrapper> {
this.configuration = new JSONConfigWrapper(map); this.configuration = new JSONConfigWrapper(map);
} }
@Override
public @NotNull JSONConfigWrapper getConfiguration() {
return this.configuration;
}
@Override
protected void onReload() throws Exception {
super.reload();
initializeConfig();
}
@Override @Override
public @Nullable ConfigurationComments getComments() { public @Nullable ConfigurationComments getComments() {
return null; return null;
@@ -68,7 +68,7 @@ public class JSONConfigProvider extends FileConfigProvider<JSONConfigWrapper> {
@Override @Override
public void save() throws Exception { public void save() throws Exception {
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) { try (Writer writer = new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8)) {
gson.toJson(configuration.data, writer); gson.toJson(configuration.data, writer);
} }
} }
+50 -2
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>3.7.1</version> <version>3.9.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -15,7 +15,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding> <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.deploy.skip>true</maven.deploy.skip> <log4j.version>2.22.0</log4j.version>
</properties> </properties>
<artifactId>easyconfiguration-sql</artifactId> <artifactId>easyconfiguration-sql</artifactId>
@@ -28,6 +28,54 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </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> </dependencies>
</project> </project>
@@ -1,8 +1,26 @@
package cc.carm.lib.configuration; 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 { public class EasyConfiguration {
private 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");
}
} }
@@ -3,36 +3,184 @@ package cc.carm.lib.configuration.sql;
import cc.carm.lib.configuration.core.ConfigInitializer; import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.core.source.ConfigurationComments; import cc.carm.lib.configuration.core.source.ConfigurationComments;
import cc.carm.lib.configuration.core.source.ConfigurationProvider; 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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SQLConfigProvider extends ConfigurationProvider<SQLSectionWrapper> { import java.sql.ResultSet;
import java.util.*;
public class SQLConfigProvider extends ConfigurationProvider<SQLConfigWrapper> {
@Override public static Gson GSON = new GsonBuilder().serializeNulls().disableHtmlEscaping().setPrettyPrinting().create();
public @NotNull SQLSectionWrapper getConfiguration() {
return null; 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 @Override
public void save() throws Exception { public @NotNull SQLConfigWrapper getConfiguration() {
return rootConfiguration;
}
public void initializeConfig() throws Exception {
this.table.create(this.sqlManager);
this.initializer = new ConfigInitializer<>(this);
onReload();
} }
@Override @Override
protected void onReload() throws Exception { 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 @Override
public @Nullable ConfigurationComments getComments() { public void save() throws Exception {
return null; 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 @Override
public @NotNull ConfigInitializer<? extends ConfigurationProvider<SQLSectionWrapper>> getInitializer() { public @NotNull ConfigInitializer<? extends ConfigurationProvider<SQLConfigWrapper>> getInitializer() {
return null; 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);
}
}
@@ -1,63 +0,0 @@
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.List;
import java.util.Map;
import java.util.Set;
public class SQLSectionWrapper implements ConfigurationWrapper<Map<String, Object>> {
@Override
public @NotNull Map<String, Object> getSource() {
return null;
}
@Override
public @NotNull Set<String> getKeys(boolean deep) {
return null;
}
@Override
public @NotNull Map<String, Object> getValues(boolean deep) {
return null;
}
@Override
public void set(@NotNull String path, @Nullable Object value) {
}
@Override
public boolean contains(@NotNull String path) {
return false;
}
@Override
public @Nullable Object get(@NotNull String path) {
return null;
}
@Override
public boolean isList(@NotNull String path) {
return false;
}
@Override
public @Nullable List<?> getList(@NotNull String path) {
return null;
}
@Override
public boolean isConfigurationSection(@NotNull String path) {
return false;
}
@Override
public @Nullable SQLSectionWrapper getConfigurationSection(@NotNull String path) {
return null;
}
}
@@ -1,7 +0,0 @@
package cc.carm.lib.configuration.sql;
public class SQLValueParser {
}
@@ -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);
}
}
+19
View File
@@ -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>
+9 -9
View File
@@ -1,14 +1,14 @@
CREATE TABLE IF NOT EXISTS conf CREATE TABLE IF NOT EXISTS conf
( (
`namespace` VARCHAR(255) NOT NULL, # `namespace` VARCHAR(32) NOT NULL, # ()
`section` VARCHAR(255) NOT NULL, # (ConfigPath) `path` VARCHAR(96) NOT NULL, # (ConfigPath)
`type` VARCHAR(255) NOT NULL, # (Integer/Byte/List/Map/...) `type` TINYINT UNSIGNED NOT NULL DEFAULT 0, # (Integer/Byte/List/Map/...)
`value` MEDIUMTEXT, # (JSON格式) `value` MEDIUMTEXT, # (JSON格式)
`inline_comments` TINYTEXT, # `inline_comments` TEXT, #
`header_comments` TEXT, # `header_comments` MEDIUMTEXT, #
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, #
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`namespace`, `section`) PRIMARY KEY (`namespace`, `path`)
) ENGINE = InnoDB ) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4; DEFAULT CHARSET = utf8mb4;
+3 -2
View File
@@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<version>3.7.1</version> <version>3.9.0</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@@ -41,10 +41,11 @@
<version>${project.parent.version}</version> <version>${project.parent.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.bspfsystems</groupId> <groupId>org.bspfsystems</groupId>
<artifactId>yamlconfiguration</artifactId> <artifactId>yamlconfiguration</artifactId>
<version>1.3.3</version> <version>2.0.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
@@ -10,6 +10,8 @@ import config.source.ModelConfiguration;
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerialization; import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerialization;
import org.junit.Test; import org.junit.Test;
import java.io.IOException;
public class DemoConfigTest { public class DemoConfigTest {
static { static {
@@ -17,7 +19,7 @@ public class DemoConfigTest {
ConfigurationSerialization.registerClass(AnyModel.class); ConfigurationSerialization.registerClass(AnyModel.class);
} }
protected final YAMLConfigProvider provider = EasyConfiguration.from("target/config.yml", "config.yml"); protected final YAMLConfigProvider provider = EasyConfiguration.from("target/config.yml", "test/test2/config.yml");
@Test @Test
public void onTest() { public void onTest() {
@@ -42,6 +44,8 @@ public class DemoConfigTest {
if (anyModel != null) System.out.println(anyModel.getName()); if (anyModel != null) System.out.println(anyModel.getName());
ModelConfiguration.MODELS.forEach(System.out::println); ModelConfiguration.MODELS.forEach(System.out::println);
ModelConfiguration.MODEL_MAP.forEach((v, anyModel1) -> System.out.println(v + " -> " + anyModel1.toString()));
System.out.println("----------------------------------------------------"); System.out.println("----------------------------------------------------");
} }
@@ -5,13 +5,13 @@ import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.annotation.HeaderComment; import cc.carm.lib.configuration.core.annotation.HeaderComment;
import cc.carm.lib.configuration.core.value.ConfigValue; 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.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.demo.tests.model.AbstractModel;
import cc.carm.lib.configuration.yaml.value.ConfiguredSerializable; import cc.carm.lib.configuration.yaml.value.ConfiguredSerializable;
import config.model.AnyModel; import config.model.AnyModel;
import config.model.SomeModel; import config.model.SomeModel;
import java.util.Map;
@HeaderComment("以下内容用于测试序列化") @HeaderComment("以下内容用于测试序列化")
@ConfigPath("model-test") @ConfigPath("model-test")
public class ModelConfiguration extends ConfigurationRoot { public class ModelConfiguration extends ConfigurationRoot {
@@ -25,10 +25,19 @@ public class ModelConfiguration extends ConfigurationRoot {
); );
public static final ConfiguredList<AnyModel> MODELS = ConfiguredList.builderOf(AnyModel.class) public static final ConfiguredList<AnyModel> MODELS = ConfiguredList.builderOf(AnyModel.class)
.fromObject() .fromMap()
.parseValue((section) -> AnyModel.deserialize((Map<String, ?>) section)) .parseValue(AnyModel::deserialize).serializeValue(AnyModel::serialize)
.serializeValue(AnyModel::serialize)
.defaults(AnyModel.random(), AnyModel.random(), AnyModel.random()) .defaults(AnyModel.random(), AnyModel.random(), AnyModel.random())
.build(); .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();
} }
+6 -6
View File
@@ -15,7 +15,7 @@
<groupId>cc.carm.lib</groupId> <groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId> <artifactId>easyconfiguration-parent</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>3.7.1</version> <version>3.9.0</version>
<modules> <modules>
<module>core</module> <module>core</module>
<module>demo</module> <module>demo</module>
@@ -110,7 +110,7 @@
<dependency> <dependency>
<groupId>org.jetbrains</groupId> <groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId> <artifactId>annotations</artifactId>
<version>24.0.1</version> <version>24.1.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@@ -123,7 +123,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version> <version>3.2.3</version>
<configuration> <configuration>
<useSystemClassLoader>false</useSystemClassLoader> <useSystemClassLoader>false</useSystemClassLoader>
</configuration> </configuration>
@@ -167,7 +167,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version> <version>3.6.3</version>
<configuration> <configuration>
<classifier>javadoc</classifier> <classifier>javadoc</classifier>
<detectJavaApiLink>false</detectJavaApiLink> <detectJavaApiLink>false</detectJavaApiLink>
@@ -194,7 +194,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version> <version>3.12.0</version>
<configuration> <configuration>
<source>${project.jdk.version}</source> <source>${project.jdk.version}</source>
<target>${project.jdk.version}</target> <target>${project.jdk.version}</target>
@@ -226,7 +226,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.5.0</version> <version>3.5.1</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>