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

Compare commits

...

35 Commits

Author SHA1 Message Date
carm 8eefba5159 feat(init): 支持针对于实体类的初始化 #9 2022-12-11 02:17:30 +08:00
carm 01e20df559 feat(init): 支持针对于实体类的初始化 #9 2022-12-11 02:04:23 +08:00
carm 35398ab741 feat(init): 支持针对于实体类的初始化 #9 2022-12-11 02:02:16 +08:00
carm 6f97166192 fix(parse): 修复Number基础类型转型丢失的问题。 fix #17 2022-12-06 21:53:05 +08:00
dependabot[bot] ccd239ad6b chore(deps): Bump yamlconfiguration from 1.2.2 to 1.3.0 (#16)
Bumps [yamlconfiguration](https://github.com/bspfsystems/YamlConfiguration) from 1.2.2 to 1.3.0.
- [Release notes](https://github.com/bspfsystems/YamlConfiguration/releases)
- [Commits](https://github.com/bspfsystems/YamlConfiguration/compare/v1.2.2...v1.3.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-03 19:50:13 +08:00
dependabot[bot] 69b27476bc chore(deps): Bump maven-shade-plugin from 3.4.0 to 3.4.1 (#15)
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.4.0...maven-shade-plugin-3.4.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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-02 18:31:07 +08:00
dependabot[bot] 0651cac6b0 chore(deps): Bump gson from 2.9.1 to 2.10 (#14)
Bumps [gson](https://github.com/google/gson) from 2.9.1 to 2.10.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.9.1...gson-parent-2.10)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-27 00:00:49 +08:00
dependabot[bot] 1a1efad283 Bump maven-shade-plugin from 3.3.0 to 3.4.0 (#11)
Bumps [maven-shade-plugin](https://github.com/apache/maven-shade-plugin) from 3.3.0 to 3.4.0.
- [Release notes](https://github.com/apache/maven-shade-plugin/releases)
- [Commits](https://github.com/apache/maven-shade-plugin/compare/maven-shade-plugin-3.3.0...maven-shade-plugin-3.4.0)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-20 00:12:03 +08:00
dependabot[bot] 3c1ba61b61 Bump yamlconfiguration from 1.2.1 to 1.2.2 (#10)
Bumps [yamlconfiguration](https://github.com/bspfsystems/YamlConfiguration) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/bspfsystems/YamlConfiguration/releases)
- [Commits](https://github.com/bspfsystems/YamlConfiguration/compare/v1.2.1...v1.2.2)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-20 00:11:44 +08:00
dependabot[bot] 1a3e84a787 Bump maven-jar-plugin from 3.2.2 to 3.3.0 (#12)
Bumps [maven-jar-plugin](https://github.com/apache/maven-jar-plugin) from 3.2.2 to 3.3.0.
- [Release notes](https://github.com/apache/maven-jar-plugin/releases)
- [Commits](https://github.com/apache/maven-jar-plugin/compare/maven-jar-plugin-3.2.2...maven-jar-plugin-3.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-20 00:11:31 +08:00
carm 00228db2c4 chore(dev): 取消开发中模块的部署 2022-09-10 01:11:04 +08:00
carm 0fddfe28af feat(source): 为 ConfigurationWrapper 添加 getSource() 方法以获取源实现内容。
BREAKING-CHANGE: ConfigurationWrapper 更改为泛型类,并新增 “getSource” 方法需要实现。
2022-09-10 00:36:46 +08:00
dependabot[bot] 4a17089da0 Bump gson from 2.9.0 to 2.9.1 (#6)
Bumps [gson](https://github.com/google/gson) from 2.9.0 to 2.9.1.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/master/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.9.0...gson-parent-2.9.1)

---
updated-dependencies:
- dependency-name: com.google.code.gson:gson
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-09 21:54:36 +08:00
dependabot[bot] 8faa7b1c24 Bump yamlconfiguration from 1.2.0 to 1.2.1 (#7)
Bumps [yamlconfiguration](https://github.com/bspfsystems/YamlConfiguration) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/bspfsystems/YamlConfiguration/releases)
- [Commits](https://github.com/bspfsystems/YamlConfiguration/compare/v1.2.0...v1.2.1)

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

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-09 21:54:25 +08:00
dependabot[bot] 5e525428fe Bump maven-javadoc-plugin from 3.4.0 to 3.4.1 (#8)
Bumps [maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.4.0...maven-javadoc-plugin-3.4.1)

---
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>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-09 21:54:16 +08:00
carm bc0dfd5698 feat(source): 为 ConfigurationWrapper 添加 getSource() 方法以获取源实现内容。
BREAKING-CHANGE: ConfigurationWrapper 更改为泛型类,并新增 “getSource” 方法需要实现。
2022-09-09 21:54:05 +08:00
carm dc28d743db feat(source): 为 ConfigurationWrapper 添加 getSource() 方法以获取源实现内容。
BREAKING-CHANGE: ConfigurationWrapper 更改为泛型类,并新增 “getSource” 方法需要实现。
2022-09-09 21:52:26 +08:00
carm f61294c5f3 feat(sql): 优化数据表结构 2022-08-11 17:43:42 +08:00
carm 6883a464db feat(sql): 添加数据库存储支持 2022-08-11 16:02:12 +08:00
carm bcdf0d9bd1 refactor(comments): 优化注释的存储方式 2022-08-11 16:01:27 +08:00
carm 5f89ff4db7 Merge remote-tracking branch 'origin/master' 2022-05-20 18:18:47 +08:00
carm d6f4970277 [3.1.0] fix(api): configuration reload problem
BREAKING CHANGES: Now we override "onReload()" method to define ConfigurationProvider how to reload the configs.

Fix the problem that when the configuration file is read, the "reload()" method not working.
2022-05-20 18:16:44 +08:00
carm c045ca1489 Merge pull request #5 from CarmJos/dependabot/maven/org.bspfsystems-yamlconfiguration-1.2.0
Bump yamlconfiguration from 1.1.0 to 1.2.0
2022-05-20 10:10:38 +08:00
dependabot[bot] ceea900b08 Bump yamlconfiguration from 1.1.0 to 1.2.0
Bumps [yamlconfiguration](https://github.com/bspfsystems/YamlConfiguration) from 1.1.0 to 1.2.0.
- [Release notes](https://github.com/bspfsystems/YamlConfiguration/releases)
- [Commits](https://github.com/bspfsystems/YamlConfiguration/compare/v1.1.0...v1.2.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>
2022-05-19 14:41:47 +00:00
carm 85bacb24f3 Merge remote-tracking branch 'origin/master' 2022-05-18 23:23:25 +08:00
carm 6b3a353fcc [3.0.0] (breaking-update) update comments usage. 2022-05-18 23:23:11 +08:00
carm 2c026fc0b0 Merge pull request #4 from CarmJos/dependabot/maven/org.bspfsystems-yamlconfiguration-1.1.0
Bump yamlconfiguration from 1.0.11 to 1.1.0
2022-04-25 22:59:37 +08:00
dependabot[bot] f8b4bbd3a9 Bump yamlconfiguration from 1.0.11 to 1.1.0
Bumps [yamlconfiguration](https://github.com/bspfsystems/YamlConfiguration) from 1.0.11 to 1.1.0.
- [Release notes](https://github.com/bspfsystems/YamlConfiguration/releases)
- [Commits](https://github.com/bspfsystems/YamlConfiguration/compare/v1.0.11...v1.1.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>
2022-04-25 14:45:52 +00:00
carm e9a0f0ff30 [ci skip] 补充README介绍 2022-04-23 21:37:25 +08:00
carm e3fe6e7c80 [ci skip] 补充README介绍 2022-04-23 21:37:18 +08:00
carm c179fa2ccd 添加部分注释 2022-04-23 20:52:08 +08:00
carm 390815b790 [2.3.0] 版本更新
- [U] 基于 tchristofferson/ConfigUpdater 项目重写YAML相关配置文件的注释部分。
- [A] 为 @ConfigComment 注解添加 ”statWrap“ 与 "endWrap" 两个选项,用于实现不同样式的注释。
2022-04-23 20:35:48 +08:00
carm 760ac815df [skip ci] 修改协议 2022-04-22 17:54:59 +08:00
carm 216050a701 [skip ci] 修改部分代码格式 2022-04-22 17:50:28 +08:00
carm 6d0ee35197 [skip ci] 修改开源协议 2022-04-22 17:46:28 +08:00
69 changed files with 1272 additions and 715 deletions
+19 -22
View File
@@ -9,7 +9,7 @@
# 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://opensource.org/licenses/MIT)
[![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)
@@ -20,6 +20,9 @@
## 优势
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
- 支持复杂配置的手动序列化、反序列化。
- 提供多种builder形式,快速构建 `ConfigValue<?>` 对象。
- 支持通过注解规定配置对应的路径、注释等信息。
## 开发
@@ -27,7 +30,7 @@
### 示例代码
您可以 [点击这里](impl/yaml/src/test/java/config/source/DemoConfiguration.java) 查看部分代码演示,更多演示详见 [开发介绍](.documentation/README.md) 。
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java) 查看部分代码演示,更多演示详见 [开发介绍](.documentation/README.md) 。
### 依赖方式
@@ -91,6 +94,15 @@
<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>
```
@@ -131,6 +143,10 @@ dependencies {
//基于YAML文件的实现版本,可用于全部Java环境。
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
//基于JSON文件的实现版本,可用于全部Java环境。
//需要注意的是,JSON不支持文件注释。
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
}
```
@@ -155,23 +171,4 @@ EasyConfiguration for MineCraft!
## 开源协议
本项目源码采用 [The MIT License](https://opensource.org/licenses/MIT) 开源协议。
<details>
<summary>关于 MIT 协议</summary>
> MIT 协议可能是几大开源协议中最宽松的一个,核心条款是:
>
> 该软件及其相关文档对所有人免费,可以任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。唯一的限制是,软件中必须包含上述版 权和许可提示。
>
> 这意味着:
>
> - 你可以自由使用,复制,修改,可以用于自己的项目。
> - 可以免费分发或用来盈利。
> - 唯一的限制是必须包含许可声明。
>
> MIT 协议是所有开源许可中最宽松的一个,除了必须包含许可声明外,再无任何限制。
>
> *以上文字来自 [五种开源协议GPL,LGPL,BSD,MIT,Apache](https://www.oschina.net/question/54100_9455) 。*
</details>
本项目源码采用 [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html) 开源协议。
+3 -1
View File
@@ -5,12 +5,14 @@
<parent>
<artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>2.2.0</version>
<version>3.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<artifactId>easyconfiguration-core</artifactId>
@@ -1,13 +1,16 @@
package cc.carm.lib.configuration.core;
import cc.carm.lib.configuration.core.annotation.ConfigComment;
import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.annotation.HeaderComment;
import cc.carm.lib.configuration.core.annotation.InlineComment;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
/**
* 配置文件类初始化方法
@@ -43,8 +46,8 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
*/
public void initialize(@NotNull Class<? extends ConfigurationRoot> clazz,
boolean saveDefaults, boolean loadSubClasses) {
initializeClass(
clazz, null,
initializeStaticClass(
clazz, null, null,
null, null, null,
saveDefaults, loadSubClasses
);
@@ -57,81 +60,154 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
}
}
protected void initializeClass(@NotNull Class<?> clazz,
@Nullable String parentPath, @Nullable String fieldName,
@Nullable ConfigPath fieldPath, @Nullable ConfigComment filedComments,
boolean saveDefaults, boolean loadSubClasses) {
/**
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。
*
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。
*/
public void initialize(@NotNull ConfigurationRoot config) {
initialize(config, true);
}
/**
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。
*
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。
* @param saveDefaults 是否写入默认值(默认为 true)。
*/
public void initialize(@NotNull ConfigurationRoot config, boolean saveDefaults) {
initializeInstance(
config, null, null,
null, null, null,
saveDefaults
);
if (saveDefaults) {
try {
provider.save();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 针对实例类的初始化方法
private void initializeInstance(@NotNull ConfigurationRoot root,
@Nullable String parentPath, @Nullable String fieldName,
@Nullable ConfigPath fieldPath,
@Nullable HeaderComment fieldHeaderComments,
@Nullable InlineComment fieldInlineComments,
boolean saveDefaults) {
String path = getClassPath(root.getClass(), parentPath, fieldName, fieldPath);
this.provider.setHeaderComment(path, getClassHeaderComments(root.getClass(), fieldHeaderComments));
if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments));
for (Field field : root.getClass().getDeclaredFields()) {
initializeField(root, field, path, saveDefaults, false);
}
}
// 针对静态类的初始化方法
private void initializeStaticClass(@NotNull Class<?> clazz,
@Nullable String parentPath, @Nullable String fieldName,
@Nullable ConfigPath fieldPath,
@Nullable HeaderComment fieldHeaderComments,
@Nullable InlineComment fieldInlineComments,
boolean saveDefaults, boolean loadSubClasses) {
if (!ConfigurationRoot.class.isAssignableFrom(clazz)) return; // 只解析继承了 ConfigurationRoot 的类
String path = getClassPath(clazz, parentPath, fieldName, fieldPath);
if (path != null) setComments(path, getClassComments(clazz, filedComments));
this.provider.setHeaderComment(path, getClassHeaderComments(clazz, fieldHeaderComments));
if (path != null) this.provider.setInlineComment(path, readInlineComments(fieldInlineComments));
for (Field field : clazz.getDeclaredFields()) {
initializeField(clazz, field, path, saveDefaults, loadSubClasses);
}
if (!loadSubClasses) return;
Class<?>[] classes = clazz.getDeclaredClasses();
if (loadSubClasses && classes.length > 0) {
// 逆向加载,保持顺序。
for (int i = classes.length - 1; i >= 0; i--) {
initializeClass(
classes[i], path, classes[i].getSimpleName(),
null, null,
saveDefaults, true
);
}
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
initializeStaticClass(
classes[i], path, classes[i].getSimpleName(),
null, null, null,
saveDefaults, true
);
}
}
protected void initializeValue(@NotNull ConfigValue<?> value, @NotNull String path,
@NotNull String[] comments, boolean saveDefaults) {
value.initialize(provider, saveDefaults, path, comments);
}
private void initializeField(@NotNull Class<?> source, @NotNull Field field, @Nullable String parent,
boolean saveDefaults, boolean loadSubClasses) {
private void initializeField(@NotNull Object source, @NotNull Field field,
@Nullable String parent, boolean saveDefaults, boolean loadSubClasses) {
try {
field.setAccessible(true);
Object object = field.get(source);
if (object instanceof ConfigValue<?>) {
String path = getFieldPath(field, parent);
String[] comments = readComments(field.getAnnotation(ConfigComment.class));
initializeValue((ConfigValue<?>) object, path, comments, saveDefaults);
setComments(path, comments);
} else if (object instanceof Class<?>) {
initializeClass(
initializeValue(
(ConfigValue<?>) object, getFieldPath(field, parent),
field.getAnnotation(HeaderComment.class),
field.getAnnotation(InlineComment.class),
saveDefaults
);
} else if (source instanceof ConfigurationRoot && object instanceof ConfigurationRoot) {
// 当且仅当 源字段与字段 均为ConfigurationRoot实例时,才对目标字段进行下一步初始化加载。
initializeInstance(
(ConfigurationRoot) object, parent, field.getName(),
field.getAnnotation(ConfigPath.class),
field.getAnnotation(HeaderComment.class),
field.getAnnotation(InlineComment.class),
saveDefaults
);
} else if (source instanceof Class<?> && object instanceof Class<?>) {
// 当且仅当 源字段与字段 均为静态类时,才对目标字段进行下一步初始化加载。
initializeStaticClass(
(Class<?>) object, parent, field.getName(),
field.getAnnotation(ConfigPath.class),
field.getAnnotation(ConfigComment.class),
field.getAnnotation(HeaderComment.class),
field.getAnnotation(InlineComment.class),
saveDefaults, loadSubClasses
);
}
// 以上判断实现以下规范:
// - 实例类中仅加载 ConfigValue实例 与 ConfigurationRoot实例
// - 静态类中仅加载 静态ConfigValue实例 与 静态ConfigurationRoot类
} catch (IllegalAccessException ignored) {
}
}
protected void setComments(@NotNull String path, @Nullable ConfigComment filedComments) {
setComments(path, readComments(filedComments));
protected void initializeValue(@NotNull ConfigValue<?> value, @NotNull String path,
@Nullable HeaderComment fieldHeaderComment,
@Nullable InlineComment fieldInlineComment,
boolean saveDefaults) {
value.initialize(
provider, saveDefaults, path,
readHeaderComments(fieldHeaderComment),
readInlineComments(fieldInlineComment)
);
}
protected void setComments(@NotNull String path, @NotNull String[] comments) {
if (comments.length <= 0) return;
this.provider.setComments(path, comments);
protected static @Nullable List<String> getClassHeaderComments(@NotNull Class<?> clazz,
@Nullable HeaderComment fieldAnnotation) {
List<String> classComments = readHeaderComments(clazz.getAnnotation(HeaderComment.class));
if (classComments != null) return classComments;
else return readHeaderComments(fieldAnnotation);
}
protected static @NotNull String[] readComments(@Nullable ConfigComment filedComments) {
if (filedComments == null) return new String[0];
if (String.join("", filedComments.value()).length() <= 0) return new String[0];
return filedComments.value();
protected static List<String> readHeaderComments(@Nullable HeaderComment annotation) {
if (annotation == null) return null;
String[] value = annotation.value();
return value.length > 0 ? Arrays.asList(value) : null;
}
protected static @NotNull String[] getClassComments(@NotNull Class<?> clazz,
@Nullable ConfigComment fieldAnnotation) {
String[] clazzComments = readComments(clazz.getAnnotation(ConfigComment.class));
if (clazzComments.length > 0) return clazzComments;
else return readComments(fieldAnnotation);
protected static @Nullable String readInlineComments(@Nullable InlineComment annotation) {
if (annotation == null) return null;
String value = annotation.value();
return value.length() > 0 ? value : null;
}
protected static @Nullable String getClassPath(@NotNull Class<?> clazz,
protected static @Nullable String getClassPath(@Nullable Class<?> clazz,
@Nullable String parentPath,
@Nullable String filedName,
@Nullable ConfigPath fieldAnnotation) {
@@ -139,11 +215,13 @@ public class ConfigInitializer<T extends ConfigurationProvider<?>> {
boolean fromRoot = false;
// 先获取 Class 对应的路径注解 其优先度最高。
ConfigPath clazzAnnotation = clazz.getAnnotation(ConfigPath.class);
if (clazzAnnotation != null) {
fromRoot = clazzAnnotation.root();
if (clazzAnnotation.value().length() > 0) {
return (fromRoot ? "" : parent) + clazzAnnotation.value();
if (clazz != null) {
ConfigPath clazzAnnotation = clazz.getAnnotation(ConfigPath.class);
if (clazzAnnotation != null) {
fromRoot = clazzAnnotation.root();
if (clazzAnnotation.value().length() > 0) {
return (fromRoot ? "" : parent) + clazzAnnotation.value();
}
}
}
@@ -1,17 +0,0 @@
package cc.carm.lib.configuration.core.annotation;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ConfigComment {
@NotNull
String[] value() default "";
}
@@ -0,0 +1,39 @@
package cc.carm.lib.configuration.core.annotation;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 顶部注释,用于给对应配置的顶部添加注释,便于使用者阅读查看。
* <p>如:
* <blockquote><pre>
* # 注释第一行
* # 注释第二行
* foo: "bar"
* </pre></blockquote>
*/
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface HeaderComment {
/**
* 注释内容,若内容长度为0则会视为一个空行。
* <p> 如 <b>{"foo","","bar"}</b>
* 会被添加为
* <blockquote><pre>
* # foo
*
* # bar
* foo: "bar"
* </pre></blockquote>
*
* @return 注释内容
*/
@NotNull
String[] value() default "";
}
@@ -0,0 +1,33 @@
package cc.carm.lib.configuration.core.annotation;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 行内注释,用于给对应配置的所在行添加注释,便于使用者阅读查看。
* 如:
* <blockquote><pre>
* foo: "bar" # 注释内容
* </pre></blockquote>
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface InlineComment {
/**
* 注释内容,若内容长度为0则不会添加注释
* <p> 如 <b>"foobar"</b> 将被设定为
* <blockquote><pre>
* foo: "bar" # foobar
* </pre></blockquote>
*
* @return 注释内容
*/
@NotNull
String value() default "";
}
@@ -5,6 +5,9 @@ import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
public abstract class AbstractConfigBuilder<T, B extends AbstractConfigBuilder<T, B, P>, P extends ConfigurationProvider<?>> {
protected final Class<? super P> providerClass;
@@ -12,7 +15,9 @@ public abstract class AbstractConfigBuilder<T, B extends AbstractConfigBuilder<T
protected @Nullable P provider;
protected @Nullable String path;
protected @NotNull String[] comments = new String[0];
protected @Nullable List<String> headerComments;
protected @Nullable String inlineComment;
protected @Nullable T defaultValue;
public AbstractConfigBuilder(Class<? super P> providerClass) {
@@ -34,7 +39,20 @@ public abstract class AbstractConfigBuilder<T, B extends AbstractConfigBuilder<T
}
public @NotNull B comments(@NotNull String... comments) {
this.comments = comments;
return headerComments(comments);
}
public @NotNull B headerComments(@NotNull String... comments) {
return headerComments(Arrays.asList(comments));
}
public @NotNull B headerComments(@NotNull List<String> comments) {
this.headerComments = comments;
return getThis();
}
public @NotNull B inlineComment(@NotNull String comment) {
this.inlineComment = comment;
return getThis();
}
@@ -43,5 +61,4 @@ public abstract class AbstractConfigBuilder<T, B extends AbstractConfigBuilder<T
return getThis();
}
}
@@ -1,46 +1,12 @@
package cc.carm.lib.configuration.core.builder;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class CommonConfigBuilder<T, B extends CommonConfigBuilder<T, B>>
extends AbstractConfigBuilder<T, B, ConfigurationProvider<?>> {
protected @Nullable ConfigurationProvider<?> provider;
protected @Nullable String path;
protected @NotNull String[] comments = new String[0];
protected @Nullable T defaultValue;
public CommonConfigBuilder() {
super(ConfigurationProvider.class);
}
protected abstract @NotNull B getThis();
public abstract @NotNull ConfigValue<?> build();
public @NotNull B from(@Nullable ConfigurationProvider<?> provider) {
this.provider = provider;
return getThis();
}
public @NotNull B path(@Nullable String path) {
this.path = path;
return getThis();
}
public @NotNull B comments(@NotNull String... comments) {
this.comments = comments;
return getThis();
}
public @NotNull B defaults(@Nullable T defaultValue) {
this.defaultValue = defaultValue;
return getThis();
}
}
@@ -1,8 +1,10 @@
package cc.carm.lib.configuration.core.builder.list;
import cc.carm.lib.configuration.core.annotation.InlineComment;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import org.jetbrains.annotations.NotNull;
public class ConfigListBuilder<V> {
protected final @NotNull Class<V> valueClass;
@@ -65,7 +65,8 @@ public class SourceListBuilder<S, V> extends CommonConfigBuilder<List<V>, Source
@Override
public @NotNull ConfiguredList<V> build() {
return new ConfiguredList<>(
this.provider, this.path, this.comments,
this.provider, this.path,
this.headerComments, this.inlineComment,
this.valueClass, this.defaultValue,
this.sourceParser.andThen(this.valueParser),
this.valueSerializer.andThen(sourceSerializer)
@@ -89,7 +89,8 @@ public class SourceMapBuilder<M extends Map<K, V>, S, K, V> extends CommonConfig
@Override
public @NotNull ConfiguredMap<K, V> build() {
return new ConfiguredMap<>(
this.provider, this.path, this.comments,
this.provider, this.path,
this.headerComments, this.inlineComment,
this.defaultValue, this.supplier,
this.keyClass, this.keyParser,
this.valueClass, this.sourceParser.andThen(this.valueParser),
@@ -19,7 +19,7 @@ public class ConfigValueBuilder<V> {
return fromSection(ConfigValueParser.required(), ConfigDataFunction.required());
}
public @NotNull SectionValueBuilder<V> fromSection(@NotNull ConfigValueParser<ConfigurationWrapper, V> valueParser,
public @NotNull SectionValueBuilder<V> fromSection(@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> valueParser,
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer) {
return new SectionValueBuilder<>(this.valueClass, valueParser, valueSerializer);
}
@@ -15,11 +15,11 @@ public class SectionValueBuilder<V>
protected final @NotNull Class<V> valueClass;
protected @NotNull ConfigValueParser<ConfigurationWrapper, V> parser;
protected @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser;
protected @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
public SectionValueBuilder(@NotNull Class<V> valueClass,
@NotNull ConfigValueParser<ConfigurationWrapper, V> parser,
@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser,
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
this.valueClass = valueClass;
this.parser = parser;
@@ -32,7 +32,7 @@ public class SectionValueBuilder<V>
return this;
}
public @NotNull SectionValueBuilder<V> parseValue(ConfigValueParser<ConfigurationWrapper, V> valueParser) {
public @NotNull SectionValueBuilder<V> parseValue(ConfigValueParser<ConfigurationWrapper<?>, V> valueParser) {
this.parser = valueParser;
return this;
}
@@ -45,7 +45,8 @@ public class SectionValueBuilder<V>
@Override
public @NotNull ConfiguredSection<V> build() {
return new ConfiguredSection<>(
this.provider, this.path, this.comments,
this.provider, this.path,
this.headerComments, this.inlineComment,
this.valueClass, this.defaultValue,
this.parser, this.serializer
);
@@ -57,7 +57,8 @@ public class SourceValueBuilder<S, V> extends CommonConfigBuilder<V, SourceValue
@Override
public @NotNull ConfiguredValue<V> build() {
return new ConfiguredValue<>(
this.provider, this.path, this.comments,
this.provider, this.path,
this.headerComments, this.inlineComment,
this.valueClass, this.defaultValue,
this.valueParser.compose(this.sourceParser),
this.valueSerializer.andThen(sourceSerializer)
@@ -50,6 +50,25 @@ public interface ConfigValueParser<T, R> {
@Contract(pure = true)
static <V> @NotNull ConfigValueParser<Object, V> castObject(Class<V> valueClass) {
return (input, defaultValue) -> {
if (Number.class.isAssignableFrom(valueClass)) {
if (Long.class.isAssignableFrom(valueClass)) {
input = longValue().parse(input, (Long) defaultValue);
} else if (Integer.class.isAssignableFrom(valueClass)) {
input = intValue().parse(input, (Integer) defaultValue);
} else if (Float.class.isAssignableFrom(valueClass)) {
input = floatValue().parse(input, (Float) defaultValue);
} else if (Double.class.isAssignableFrom(valueClass)) {
input = doubleValue().parse(input, (Double) defaultValue);
} else if (Byte.class.isAssignableFrom(valueClass)) {
input = byteValue().parse(input, (Byte) defaultValue);
} else if (Short.class.isAssignableFrom(valueClass)) {
input = shortValue().parse(input, (Short) defaultValue);
}
} else if (Boolean.class.isAssignableFrom(valueClass)) {
input = booleanValue().parse(input, (Boolean) defaultValue);
}
if (valueClass.isInstance(input)) return valueClass.cast(input);
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
};
@@ -0,0 +1,51 @@
package cc.carm.lib.configuration.core.source;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.*;
public abstract class ConfigurationComments {
protected final @NotNull Map<String, List<String>> headerComments = new HashMap<>();
protected final @NotNull Map<String, String> inlineComments = new HashMap<>();
protected @NotNull Map<String, List<String>> getHeaderComments() {
return headerComments;
}
protected @NotNull Map<String, String> getInlineComments() {
return inlineComments;
}
public void setHeaderComments(@Nullable String path, @Nullable List<String> comments) {
if (comments == null) {
getHeaderComments().remove(path);
} else {
getHeaderComments().put(path, comments);
}
}
public void setInlineComment(@NotNull String path, @Nullable String comment) {
if (comment == null) {
getInlineComments().remove(path);
} else {
getInlineComments().put(path, comment);
}
}
@Nullable
@Unmodifiable
public List<String> getHeaderComment(@Nullable String path) {
return Optional.ofNullable(getHeaderComments().get(path)).map(Collections::unmodifiableList).orElse(null);
}
public @Nullable String getInlineComment(@NotNull String path) {
return getInlineComments().get(path);
}
}
@@ -2,10 +2,16 @@ package cc.carm.lib.configuration.core.source;
import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import cc.carm.lib.configuration.core.value.ConfigValue;
import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
public abstract class ConfigurationProvider<W extends ConfigurationWrapper> {
import java.util.List;
import java.util.Optional;
public abstract class ConfigurationProvider<W extends ConfigurationWrapper<?>> {
protected long updateTime;
@@ -13,36 +19,127 @@ public abstract class ConfigurationProvider<W extends ConfigurationWrapper> {
this.updateTime = System.currentTimeMillis();
}
/**
* 得到配置文件的更新(最后加载)时间。
*
* @return 更新时间
*/
public long getUpdateTime() {
return updateTime;
}
/**
* 用于 {@link CachedConfigValue} 判断缓存值是否过期(即缓存的时间早于配置文件的最后加载时间)。
*
* @param time 缓存值时的时间戳
* @return 缓存值是否过期
*/
public boolean isExpired(long time) {
return this.updateTime > time;
return getUpdateTime() > time;
}
/**
* 得到配置文件的原生功能类。
*
* @return 原生类
*/
public abstract @NotNull W getConfiguration();
public abstract void reload() throws Exception;
/**
* 重载当前配置文件。(将不会保存已修改的内容)
*
* @throws Exception 当重载出现错误时抛出
*/
public void reload() throws Exception {
onReload(); // 调用重写的Reload方法
this.updateTime = System.currentTimeMillis();
}
/**
* 将当前对配置文件的更改进行保存。
*
* @throws Exception 当保存出现错误时抛出
*/
public abstract void save() throws Exception;
public abstract void setComments(@NotNull String path, @NotNull String... comments);
/**
* 针对于不同配置文件类型所执行的重载操作。
*
* @throws Exception 当操作出现错误时抛出。
*/
protected abstract void onReload() throws Exception;
public abstract @Nullable String[] getComments(@NotNull String path);
public abstract @Nullable ConfigurationComments getComments();
public void setHeaderComment(@Nullable String path, @Nullable List<String> comments) {
if (getComments() == null) return;
getComments().setHeaderComments(path, comments);
}
public void setInlineComment(@NotNull String path, @Nullable String comment) {
if (getComments() == null) return;
getComments().setInlineComment(path, comment);
}
@Nullable
@Unmodifiable
public List<String> getHeaderComment(@Nullable String path) {
return Optional.ofNullable(getComments()).map(c -> c.getHeaderComment(path)).orElse(null);
}
public @Nullable String getInlineComment(@NotNull String path) {
return Optional.ofNullable(getComments()).map(c -> c.getInlineComment(path)).orElse(null);
}
public abstract @NotNull ConfigInitializer<? extends ConfigurationProvider<W>> getInitializer();
/**
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
*
* @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。
*/
public void initialize(Class<? extends ConfigurationRoot> configClazz) {
initialize(configClazz, true);
}
/**
* 初始化指定类以及其子类的所有 {@link ConfigValue} 对象。
*
* @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。
* @param saveDefaults 是否写入默认值(默认为 true)。
*/
public void initialize(Class<? extends ConfigurationRoot> configClazz, boolean saveDefaults) {
getInitializer().initialize(configClazz, saveDefaults);
this.getInitializer().initialize(configClazz, saveDefaults);
}
/**
* 初始化指定类的所有 {@link ConfigValue} 对象。
*
* @param configClazz 配置文件类,须继承于 {@link ConfigurationRoot} 。
* @param saveDefaults 是否写入默认值(默认为 true)。
* @param loadSubClasses 是否加载内部子类(默认为 true)。
*/
public void initialize(Class<? extends ConfigurationRoot> configClazz, boolean saveDefaults, boolean loadSubClasses) {
getInitializer().initialize(configClazz, saveDefaults, loadSubClasses);
this.getInitializer().initialize(configClazz, saveDefaults, loadSubClasses);
}
/**
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。
*
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。
*/
public void initialize(@NotNull ConfigurationRoot config) {
this.getInitializer().initialize(config, true);
}
/**
* 初始化指定实例的所有 {@link ConfigValue} 与内部 {@link ConfigurationRoot} 对象。
*
* @param config 配置文件实例类,须实现 {@link ConfigurationRoot} 。
* @param saveDefaults 是否写入默认值(默认为 true)。
*/
public void initialize(@NotNull ConfigurationRoot config, boolean saveDefaults) {
this.getInitializer().initialize(config, saveDefaults);
}
}
@@ -4,10 +4,15 @@ import cc.carm.lib.configuration.core.function.ConfigValueParser;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
interface ConfigurationReader {
ConfigurationWrapper getWrapper();
ConfigurationWrapper<?> getWrapper();
default boolean isBoolean(@NotNull String path) {
return getWrapper().isType(path, Boolean.class);
@@ -26,7 +31,6 @@ interface ConfigurationReader {
return getWrapper().isType(path, Byte.class);
}
default @Nullable Byte getByte(@NotNull String path) {
return getByte(path, (byte) 0);
}
@@ -36,12 +40,10 @@ interface ConfigurationReader {
return getWrapper().get(path, def, ConfigValueParser.byteValue());
}
default boolean isShort(@NotNull String path) {
return getWrapper().isType(path, Short.class);
}
default @Nullable Short getShort(@NotNull String path) {
return getShort(path, (short) 0);
}
@@ -56,7 +58,6 @@ interface ConfigurationReader {
return getWrapper().isType(path, Integer.class);
}
default @Nullable Integer getInt(@NotNull String path) {
return getInt(path, 0);
}
@@ -71,7 +72,6 @@ interface ConfigurationReader {
return getWrapper().isType(path, Long.class);
}
default @Nullable Long getLong(@NotNull String path) {
return getLong(path, 0L);
}
@@ -86,7 +86,6 @@ interface ConfigurationReader {
return getWrapper().isType(path, Float.class);
}
default @Nullable Float getFloat(@NotNull String path) {
return getFloat(path, 0.0F);
}
@@ -101,7 +100,6 @@ interface ConfigurationReader {
return getWrapper().isType(path, Double.class);
}
default @Nullable Double getDouble(@NotNull String path) {
return getDouble(path, 0.0D);
}
@@ -116,7 +114,6 @@ interface ConfigurationReader {
return getWrapper().isType(path, Boolean.class);
}
default @Nullable Character getChar(@NotNull String path) {
return getChar(path, null);
}
@@ -140,5 +137,54 @@ interface ConfigurationReader {
return getWrapper().get(path, def, String.class);
}
@Unmodifiable
default @NotNull List<String> getStringList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.castToString());
}
@Unmodifiable
default @NotNull List<Integer> getIntegerList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.intValue());
}
@Unmodifiable
default @NotNull List<Long> getLongList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.longValue());
}
@Unmodifiable
default @NotNull List<Double> getDoubleList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.doubleValue());
}
@Unmodifiable
default @NotNull List<Float> getFloatList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.floatValue());
}
@Unmodifiable
default @NotNull List<Byte> getByteList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.byteValue());
}
@Unmodifiable
default @NotNull List<Character> getCharList(@NotNull String path) {
return parseList(getWrapper().getList(path), ConfigValueParser.castObject(Character.class));
}
@Unmodifiable
static <T> @NotNull List<T> parseList(@Nullable List<?> list, ConfigValueParser<Object, T> parser) {
if (list == null) return Collections.emptyList();
List<T> values = new ArrayList<>();
for (Object o : list) {
try {
T parsed = parser.parse(o, null);
if (parsed != null) values.add(parsed);
} catch (Exception ignored) {
}
}
return values;
}
}
@@ -9,13 +9,15 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
public interface ConfigurationWrapper extends ConfigurationReader {
public interface ConfigurationWrapper<S> extends ConfigurationReader {
@Override
default ConfigurationWrapper getWrapper() {
default ConfigurationWrapper<S> getWrapper() {
return this;
}
@NotNull S getSource();
@NotNull
Set<String> getKeys(boolean deep);
@@ -66,6 +68,6 @@ public interface ConfigurationWrapper extends ConfigurationReader {
boolean isConfigurationSection(@NotNull String path);
@Nullable
ConfigurationWrapper getConfigurationSection(@NotNull String path);
ConfigurationWrapper<S> getConfigurationSection(@NotNull String path);
}
@@ -10,7 +10,7 @@ import java.net.URL;
import java.net.URLConnection;
import java.util.Objects;
public abstract class FileConfigProvider<W extends ConfigurationWrapper> extends ConfigurationProvider<W> {
public abstract class FileConfigProvider<W extends ConfigurationWrapper<?>> extends ConfigurationProvider<W> {
protected final @NotNull File file;
@@ -5,7 +5,9 @@ import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -17,24 +19,37 @@ public abstract class ConfigValue<T> {
protected @Nullable ConfigurationProvider<?> provider;
protected @Nullable String configPath;
protected @NotNull String[] comments;
protected @Nullable List<String> headerComments;
protected @Nullable String inlineComments;
protected @Nullable T defaultValue;
public ConfigValue(@Nullable ConfigurationProvider<?> provider,
@Nullable String configPath, @NotNull String[] comments, @Nullable T defaultValue) {
public ConfigValue(@Nullable ConfigurationProvider<?> provider, @Nullable String configPath,
@Nullable List<String> headerComments, @Nullable String inlineComments,
@Nullable T defaultValue) {
this.provider = provider;
this.configPath = configPath;
this.comments = comments;
this.headerComments = headerComments;
this.inlineComments = inlineComments;
this.defaultValue = defaultValue;
}
public void initialize(@NotNull ConfigurationProvider<?> provider, boolean saveDefault,
@NotNull String configPath, @NotNull String... comments) {
public void initialize(@NotNull ConfigurationProvider<?> provider, boolean saveDefault, @NotNull String configPath,
@Nullable List<String> headerComments, @Nullable String inlineComments) {
if (this.provider == null) this.provider = provider;
if (this.configPath == null) this.configPath = configPath;
if (this.comments.length == 0) this.comments = comments;
if (this.headerComments == null) this.headerComments = headerComments;
if (this.inlineComments == null) this.inlineComments = inlineComments;
if (saveDefault) setDefault();
if (getHeaderComments() != null) {
this.provider.setHeaderComment(getConfigPath(), getHeaderComments());
}
if (getInlineComments() != null) {
this.provider.setInlineComment(getConfigPath(), getInlineComments());
}
}
public @Nullable T getDefaultValue() {
@@ -103,6 +118,11 @@ public abstract class ConfigValue<T> {
Optional.ofNullable(getDefaultValue()).ifPresent(this::set);
}
/**
* 判断加载的配置是否与默认值相同。
*
* @return 获取当前值是否为默认值。
*/
public boolean isDefault() {
T defaultValue = getDefaultValue();
T value = get();
@@ -116,7 +136,7 @@ public abstract class ConfigValue<T> {
.orElseThrow(() -> new IllegalStateException("Value(" + configPath + ") does not have a provider."));
}
public final @NotNull ConfigurationWrapper getConfiguration() {
public final @NotNull ConfigurationWrapper<?> getConfiguration() {
try {
return getProvider().getConfiguration();
} catch (Exception ex) {
@@ -137,12 +157,13 @@ public abstract class ConfigValue<T> {
getConfiguration().set(getConfigPath(), value);
}
public String[] getComments() {
return comments;
public @Nullable String getInlineComments() {
return inlineComments;
}
public void setComments(String[] comments) {
this.comments = comments;
@Unmodifiable
public @Nullable List<String> getHeaderComments() {
return headerComments;
}
}
@@ -2,9 +2,10 @@ package cc.carm.lib.configuration.core.value.impl;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public abstract class CachedConfigValue<T> extends ConfigValue<T> {
@@ -12,8 +13,9 @@ public abstract class CachedConfigValue<T> extends ConfigValue<T> {
protected long parsedTime = -1;
public CachedConfigValue(@Nullable ConfigurationProvider<?> provider, @Nullable String sectionPath,
@NotNull String[] comments, @Nullable T defaultValue) {
super(provider, sectionPath, comments, defaultValue);
@Nullable List<String> headerComments, @Nullable String inlineComments,
@Nullable T defaultValue) {
super(provider, sectionPath, headerComments, inlineComments, defaultValue);
}
protected T updateCache(T value) {
@@ -1,5 +1,6 @@
package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.annotation.HeaderComment;
import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
@@ -16,17 +17,18 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
return builder().asList(valueClass);
}
@HeaderComment
protected final @NotNull Class<V> valueClass;
protected final @NotNull ConfigDataFunction<Object, V> parser;
protected final @NotNull ConfigDataFunction<V, Object> serializer;
public ConfiguredList(@Nullable ConfigurationProvider<?> provider,
@Nullable String sectionPath, @NotNull String[] comments,
public ConfiguredList(@Nullable ConfigurationProvider<?> provider, @Nullable String sectionPath,
@Nullable List<String> headerComments, @Nullable String inlineComments,
@NotNull Class<V> valueClass, @Nullable List<V> defaultValue,
@NotNull ConfigDataFunction<Object, V> parser,
@NotNull ConfigDataFunction<V, Object> serializer) {
super(provider, sectionPath, comments, defaultValue);
super(provider, sectionPath, headerComments, inlineComments, defaultValue);
this.valueClass = valueClass;
this.parser = parser;
this.serializer = serializer;
@@ -34,15 +36,10 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
@Override
public @NotNull List<V> get() {
if (isExpired()) { // 已过时的数据,需要重新解析一次。
List<V> list = new ArrayList<>();
List<?> data = getConfiguration().getList(getConfigPath());
if (data == null || data.isEmpty()) return useOrDefault(list);
for (Object dataVal : data) {
if (dataVal == null) continue;
try {
@@ -51,7 +48,6 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
e.printStackTrace();
}
}
return updateCache(list);
} else if (getCachedValue() != null) return getCachedValue();
else if (getDefaultValue() != null) return getDefaultValue();
@@ -9,6 +9,7 @@ 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;
import java.util.function.Supplier;
@@ -31,14 +32,14 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> {
protected final @NotNull ConfigDataFunction<K, String> keySerializer;
protected final @NotNull ConfigDataFunction<V, Object> valueSerializer;
public ConfiguredMap(@Nullable ConfigurationProvider<?> provider,
@Nullable String sectionPath, @NotNull String[] comments,
public ConfiguredMap(@Nullable ConfigurationProvider<?> provider, @Nullable String sectionPath,
@Nullable List<String> headerComments, @Nullable String inlineComments,
@Nullable Map<K, V> defaultValue, @NotNull Supplier<? extends Map<K, V>> supplier,
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<Object, V> valueParser,
@NotNull ConfigDataFunction<K, String> keySerializer,
@NotNull ConfigDataFunction<V, Object> valueSerializer) {
super(provider, sectionPath, comments, defaultValue);
super(provider, sectionPath, headerComments, inlineComments, defaultValue);
this.supplier = supplier;
this.keyClass = keyClass;
this.valueClass = valueClass;
@@ -77,7 +78,7 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> {
if (isExpired()) { // 已过时的数据,需要重新解析一次。
Map<K, V> map = supplier.get();
ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath());
ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath());
if (section == null) return useOrDefault(map);
Set<String> keys = section.getKeys(false);
@@ -109,9 +110,10 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> {
Map<String, Object> data = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : value.entrySet()) {
try {
String key = keySerializer.parse(entry.getKey());
Object val = valueSerializer.parse(entry.getValue());
data.put(key, val);
data.put(
keySerializer.parse(entry.getKey()),
valueSerializer.parse(entry.getValue())
);
} catch (Exception e) {
e.printStackTrace();
}
@@ -9,6 +9,7 @@ import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -20,15 +21,15 @@ public class ConfiguredSection<V> extends CachedConfigValue<V> {
protected final @NotNull Class<V> valueClass;
protected final @NotNull ConfigValueParser<ConfigurationWrapper, V> parser;
protected final @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser;
protected final @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
public ConfiguredSection(@Nullable ConfigurationProvider<?> provider,
@Nullable String sectionPath, @NotNull String[] comments,
public ConfiguredSection(@Nullable ConfigurationProvider<?> provider, @Nullable String sectionPath,
@Nullable List<String> headerComments, @Nullable String inlineComments,
@NotNull Class<V> valueClass, @Nullable V defaultValue,
@NotNull ConfigValueParser<ConfigurationWrapper, V> parser,
@NotNull ConfigValueParser<ConfigurationWrapper<?>, V> parser,
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
super(provider, sectionPath, comments, defaultValue);
super(provider, sectionPath, headerComments, inlineComments, defaultValue);
this.valueClass = valueClass;
this.parser = parser;
this.serializer = serializer;
@@ -38,7 +39,7 @@ public class ConfiguredSection<V> extends CachedConfigValue<V> {
return valueClass;
}
public @NotNull ConfigValueParser<ConfigurationWrapper, V> getParser() {
public @NotNull ConfigValueParser<ConfigurationWrapper<?>, V> getParser() {
return parser;
}
@@ -49,7 +50,7 @@ public class ConfiguredSection<V> extends CachedConfigValue<V> {
@Override
public @Nullable V get() {
if (isExpired()) { // 已过时的数据,需要重新解析一次。
ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath());
ConfigurationWrapper<?> section = getConfiguration().getConfigurationSection(getConfigPath());
if (section == null) return useDefault();
try {
// 若未出现错误,则直接更新缓存并返回。
@@ -69,7 +70,6 @@ public class ConfiguredSection<V> extends CachedConfigValue<V> {
else {
try {
setValue(serializer.parse(value));
// getConfiguration().createSection(getSectionPath(), serializer.parse(value));
} catch (Exception e) {
e.printStackTrace();
}
@@ -8,6 +8,7 @@ import cc.carm.lib.configuration.core.value.impl.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Optional;
public class ConfiguredValue<V> extends CachedConfigValue<V> {
@@ -29,13 +30,13 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
protected final @NotNull ConfigValueParser<Object, V> parser;
protected final @NotNull ConfigDataFunction<V, Object> serializer;
public ConfiguredValue(@Nullable ConfigurationProvider<?> provider,
@Nullable String sectionPath, @NotNull String[] comments,
public ConfiguredValue(@Nullable ConfigurationProvider<?> provider, @Nullable String sectionPath,
@Nullable List<String> headerComments, @Nullable String inlineComments,
@NotNull Class<V> valueClass, @Nullable V defaultValue,
@NotNull ConfigValueParser<Object, V> parser,
@NotNull ConfigDataFunction<V, Object> serializer) {
super(provider, sectionPath, comments, defaultValue);
super(provider, sectionPath, headerComments, inlineComments, defaultValue);
this.valueClass = valueClass;
this.parser = parser;
this.serializer = serializer;
@@ -1,4 +1,4 @@
package config.offset;
package cc.carm.test.config.offset;
import org.jetbrains.annotations.NotNull;
@@ -0,0 +1,63 @@
//package config.offset;
//
//
//import sun.misc.Unsafe;
//
//import java.lang.reflect.Field;
//import java.lang.reflect.Modifier;
//import java.util.Collections;
//import java.util.LinkedList;
//import java.util.List;
//
///**
// * @author Chris2018998
// */
//public class OffsetUtil {
// private static Unsafe unsafe;
//
// static {
// try {
// // 获取 Unsafe 内部的私有的实例化单例对象
// Field field = Unsafe.class.getDeclaredField("theUnsafe");
// // 无视权限
// field.setAccessible(true);
// unsafe = (Unsafe) field.get(null);
// } catch (NoSuchFieldException | IllegalAccessException e) {
// e.printStackTrace();
// }
//// try {
//// unsafe = AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> {
//// Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
//// theUnsafe.setAccessible(true);
//// return (Unsafe) theUnsafe.get(null);
//// });
//// } catch (Throwable e) {
//// System.err.println("Unable to load unsafe");
//// }
// }
//
// public static List<FieldOffset> getClassMemberOffset(Class<?> beanClass) {
// List<FieldOffset> offsetsList = new LinkedList<>();
// for (Field field : beanClass.getDeclaredFields()) {
// FieldOffset fieldOffset = new FieldOffset(field);
// offsetsList.add(fieldOffset);
// if (Modifier.isStatic(field.getModifiers()))
// fieldOffset.setOffsetValue(unsafe.staticFieldOffset(field));
// else
// fieldOffset.setOffsetValue(unsafe.objectFieldOffset(field));
// Class<?> fieldType = field.getType();
// if (!fieldType.getName().startsWith("java")) {
// Field[] subfields = fieldType.getDeclaredFields();
// if (subfields.length > 0) {
// fieldOffset.setSubFieldOffsetList(getClassMemberOffset(fieldType));
// }
// }
// }
//
// Collections.sort(offsetsList);
// return offsetsList;
// }
//
//
//}
//
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>3.3.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<artifactId>easyconfiguration-demo</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-core</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -1,18 +1,19 @@
package config.source;
package cc.carm.lib.configuration.demo;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import cc.carm.lib.configuration.core.annotation.ConfigComment;
import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.annotation.HeaderComment;
import cc.carm.lib.configuration.core.value.ConfigValue;
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
@ConfigComment({"数据库配置", " 用于提供数据库连接,进行数据库操作。"})
@HeaderComment({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
public class DatabaseConfiguration extends ConfigurationRoot {
@ConfigPath("driver")
@ConfigComment({
@HeaderComment({
"数据库驱动配置,请根据数据库类型设置。",
"- MySQL: com.mysql.cj.jdbc.Driver",
"- MySQL(旧): com.mysql.jdbc.Driver",
"- MySQL(新): com.mysql.cj.jdbc.Driver",
"- MariaDB(推荐): org.mariadb.jdbc.Driver",
})
protected static final ConfigValue<String> DRIVER_NAME = ConfiguredValue.of(
@@ -21,11 +22,9 @@ public class DatabaseConfiguration extends ConfigurationRoot {
protected static final ConfigValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
protected static final ConfigValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
protected static final ConfigValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
protected static final ConfigValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
protected static final ConfigValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
protected static final ConfigValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
protected static String buildJDBC() {
@@ -1,14 +1,9 @@
package config;
package cc.carm.lib.configuration.demo.tests;
import cc.carm.lib.configuration.EasyConfiguration;
import cc.carm.lib.configuration.yaml.YAMLConfigProvider;
import config.model.AbstractModel;
import config.model.SomeModel;
import config.model.TestModel;
import config.source.DemoConfiguration;
import config.source.ImplConfiguration;
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerialization;
import org.junit.Test;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.demo.tests.model.TestModel;
import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration;
import cc.carm.lib.configuration.demo.tests.conf.TestConfiguration;
import java.util.LinkedHashMap;
import java.util.List;
@@ -16,34 +11,19 @@ import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class ConfigTester {
public class ConfigurationTest {
static {
ConfigurationSerialization.registerClass(TestModel.class);
ConfigurationSerialization.registerClass(SomeModel.class);
}
protected final YAMLConfigProvider provider = EasyConfiguration.from("target/config.yml", "config.yml");
@Test
public void onTest() {
public static void testDemo(ConfigurationProvider<?> provider) {
provider.initialize(DemoConfiguration.class);
testDemo();
testSerialization();
try {
provider.save();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void testDemo() {
System.out.println("----------------------------------------------------");
System.out.println("Test Number: ");
System.out.println("before: " + DemoConfiguration.TEST_NUMBER.get());
DemoConfiguration.TEST_NUMBER.set((long) (Long.MAX_VALUE * Math.random()));
System.out.println("after: " + DemoConfiguration.TEST_NUMBER.get());
System.out.println("> Test Value:");
System.out.println("before: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get());
DemoConfiguration.Sub.UUID_CONFIG_VALUE.set(UUID.randomUUID());
@@ -68,13 +48,30 @@ public class ConfigTester {
System.out.println("----------------------------------------------------");
}
public static void testSerialization() {
System.out.println("----------------------------------------------------");
AbstractModel model = ImplConfiguration.TEST.get();
if (model != null) {
System.out.println(model.getName());
public static void testInner(ConfigurationProvider<?> provider) {
TestConfiguration TEST = new TestConfiguration();
provider.initialize(TEST, true);
System.out.println("> Test Inner value before:");
System.out.println(TEST.INNER.INNER_VALUE.getNotNull());
double after = Math.random() * 200D;
System.out.println("> Test Inner value -> " + after);
TEST.INNER.INNER_VALUE.set(after);
System.out.println("> Test Inner value after:");
System.out.println(TEST.INNER.INNER_VALUE.getNotNull());
}
public static void save(ConfigurationProvider<?> provider) {
try {
provider.save();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("----------------------------------------------------");
}
@@ -1,51 +1,52 @@
package config.source;
package cc.carm.lib.configuration.demo.tests.conf;
import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import cc.carm.lib.configuration.core.annotation.ConfigComment;
import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.annotation.HeaderComment;
import cc.carm.lib.configuration.core.annotation.InlineComment;
import cc.carm.lib.configuration.core.value.ConfigValue;
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
import config.model.TestModel;
import cc.carm.lib.configuration.demo.tests.model.TestModel;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
@HeaderComment({
"此处内容将显示在配置文件的最上方",
""/*添加一个空行与其他配置的注释分割*/
})
public class DemoConfiguration extends ConfigurationRoot {
@ConfigPath(root = true)
@ConfigComment({
"有时候,需要在配置文件最上面显示点东西,",
"此时就推荐添加一个可以用到但并不重要的参数到最上面",
"并给他添加对应的注释。"
})
protected static final ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
@ConfigPath(root = true)
public static final ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(Long.class, 1000000L);
// 支持通过 Class<?> 变量标注子配置一并注册
// 注意 若对应类也有注解则优先使用类的注解
@ConfigPath("impl-test") //支持通过注解修改子配置的主路径若不修改则以变量名自动生成
@ConfigComment("Something...") // 支持给子路径直接打注释
public static final Class<?> IMPL = ImplConfiguration.class;
// 子配置文件
@ConfigPath("database")
public static final Class<?> DB_CONFIG = DatabaseConfiguration.class;
@ConfigPath("other-class-config") //支持通过注解修改子配置的主路径若不修改则以变量名自动生成
@HeaderComment({"", "Something..."}) // 支持给子路径直接打注释
@InlineComment("InlineComments for class path")
public static final Class<?> OTHER = OtherConfiguration.class;
@ConfigPath("user") // 通过注解规定配置文件中的路径若不进行注解则以变量名自动生成
@ConfigComment({"Section类型数据测试"}) // 通过注解给配置添加注释
@HeaderComment({"", "Section类型数据测试"}) // 通过注解给配置添加注释
@InlineComment("Section数据也支持InlineComment注释")
public static final ConfigValue<TestModel> MODEL_TEST = ConfiguredSection
.builder(TestModel.class)
.defaults(new TestModel("Carm", UUID.randomUUID()))
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
.serializeValue(TestModel::serialize).build();
@ConfigComment({"[ID-UUID] 对照表", "", "用于测试Map类型的解析与序列化保存"})
@HeaderComment({"", "[ID - UUID]对照表", "", "用于测试Map类型的解析与序列化保存"})
public static final ConfigValue<Map<Integer, UUID>> USERS = ConfiguredMap
.builder(Integer.class, UUID.class).fromString()
.parseKey(Integer::parseInt)
@@ -60,6 +61,7 @@ public class DemoConfiguration extends ConfigurationRoot {
public static class Sub extends ConfigurationRoot {
@ConfigPath(value = "uuid-value", root = true)
@InlineComment("This is an inline comment")
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
.builder(UUID.class).fromString()
.parseValue((data, defaultValue) -> UUID.fromString(data))
@@ -73,7 +75,6 @@ public class DemoConfiguration extends ConfigurationRoot {
.build();
}
}
@@ -0,0 +1,4 @@
package cc.carm.lib.configuration.demo.tests.conf;
public class OtherConfiguration {
}
@@ -0,0 +1,30 @@
package cc.carm.lib.configuration.demo.tests.conf;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.annotation.HeaderComment;
import cc.carm.lib.configuration.core.annotation.InlineComment;
import cc.carm.lib.configuration.core.value.ConfigValue;
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
import cc.carm.lib.configuration.demo.tests.model.TestModel;
import java.util.UUID;
public class TestConfiguration extends ConfigurationRoot {
public final TestInnerConfiguration INNER = new TestInnerConfiguration();
public final ConfigValue<Double> CLASS_VALUE = ConfiguredValue.of(Double.class, 1.0D);
@ConfigPath("test.user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
@HeaderComment({"", "Section类型数据测试"}) // 通过注解给配置添加注释。
@InlineComment("Section数据也支持InlineComment注释")
public final ConfigValue<TestModel> TEST_MODEL = ConfiguredSection
.builder(TestModel.class)
.defaults(new TestModel("Carm", UUID.randomUUID()))
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
.serializeValue(TestModel::serialize).build();
}
@@ -0,0 +1,13 @@
package cc.carm.lib.configuration.demo.tests.conf;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import cc.carm.lib.configuration.core.annotation.HeaderComment;
import cc.carm.lib.configuration.core.value.ConfigValue;
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
@HeaderComment("Inner Test")
public class TestInnerConfiguration extends ConfigurationRoot {
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(Double.class, 1.0D);
}
@@ -1,4 +1,4 @@
package config.model;
package cc.carm.lib.configuration.demo.tests.model;
import org.jetbrains.annotations.NotNull;
@@ -1,4 +1,4 @@
package config.model;
package cc.carm.lib.configuration.demo.tests.model;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.jetbrains.annotations.NotNull;
@@ -33,7 +33,7 @@ public class TestModel extends AbstractModel {
return map;
}
public static TestModel deserialize(ConfigurationWrapper section) {
public static TestModel deserialize(ConfigurationWrapper<?> section) {
String name = section.getString("name");
if (name == null) throw new NullPointerException("name is null");
String uuidString = section.getString("info.uuid");
+11 -2
View File
@@ -5,13 +5,15 @@
<parent>
<artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>2.2.0</version>
<version>3.3.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<artifactId>easyconfiguration-json</artifactId>
<packaging>jar</packaging>
@@ -25,10 +27,17 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-demo</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.9.0</version>
<version>2.10</version>
<scope>compile</scope>
</dependency>
@@ -1,6 +1,7 @@
package cc.carm.lib.configuration.json;
import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.core.source.ConfigurationComments;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
import com.google.gson.Gson;
@@ -47,7 +48,6 @@ public class JSONConfigProvider extends FileConfigProvider<JSONConfigWrapper> {
if (map == null) map = new LinkedHashMap<>();
this.configuration = new JSONConfigWrapper(map);
this.initializer = new ConfigInitializer<>(this);
}
@Override
@@ -56,10 +56,16 @@ public class JSONConfigProvider extends FileConfigProvider<JSONConfigWrapper> {
}
@Override
public void reload() {
protected void onReload() throws Exception {
super.reload();
initializeConfig();
}
@Override
public @Nullable ConfigurationComments getComments() {
return null;
}
@Override
public void save() throws Exception {
try (Writer writer = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) {
@@ -67,16 +73,6 @@ public class JSONConfigProvider extends FileConfigProvider<JSONConfigWrapper> {
}
}
@Override
public void setComments(@NotNull String path, @NotNull String... comments) {
// JSON doesn't support comments.
}
@Override
public @Nullable String[] getComments(@NotNull String path) {
return new String[0]; // JSON doesn't support comments.
}
@Override
public @NotNull ConfigInitializer<? extends ConfigurationProvider<JSONConfigWrapper>> getInitializer() {
return this.initializer;
@@ -4,14 +4,17 @@ import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Some code comes from BungeeCord's implementation of the JsonConfiguration.
*
* @author md_5, CarmJos
*/
public class JSONConfigWrapper implements ConfigurationWrapper {
public class JSONConfigWrapper implements ConfigurationWrapper<Map<String, Object>> {
private static final char SEPARATOR = '.';
protected final Map<String, Object> data;
@@ -30,15 +33,23 @@ public class JSONConfigWrapper implements ConfigurationWrapper {
}
}
@Override
public @NotNull Map<String, Object> getSource() {
return this.data;
}
@Override
public @NotNull Set<String> getKeys(boolean deep) {
return new LinkedHashSet<>(this.data.keySet());
return getValues(deep).keySet();
}
@Override
public @NotNull Map<String, Object> getValues(boolean deep) {
return new LinkedHashMap<>(this.data);
if (deep) {
Map<String, Object> values = new LinkedHashMap<>();
mapChildrenValues(values, this, null, true);
return values;
} else return new LinkedHashMap<>(this.data);
}
@Override
@@ -87,7 +98,7 @@ public class JSONConfigWrapper implements ConfigurationWrapper {
}
@Override
public @Nullable ConfigurationWrapper getConfigurationSection(@NotNull String path) {
public @Nullable JSONConfigWrapper getConfigurationSection(@NotNull String path) {
Object val = get(path);
return (val instanceof JSONConfigWrapper) ? (JSONConfigWrapper) val : null;
}
@@ -111,4 +122,16 @@ public class JSONConfigWrapper implements ConfigurationWrapper {
return (index == -1) ? path : path.substring(index + 1);
}
protected void mapChildrenValues(@NotNull Map<String, Object> output, @NotNull JSONConfigWrapper section,
@Nullable String parent, boolean deep) {
for (Map.Entry<String, Object> entry : section.data.entrySet()) {
String path = (parent == null ? "" : parent + ".") + entry.getKey();
output.remove(path);
output.put(path, entry.getValue());
if (deep && entry.getValue() instanceof JSONConfigWrapper) {
this.mapChildrenValues(output, (JSONConfigWrapper) entry.getValue(), path, true);
}
}
}
}
@@ -1,4 +0,0 @@
package cc.carm.lib.configuration.json;
public abstract class JSONValue {
}
@@ -1,62 +1,28 @@
package config;
import cc.carm.lib.configuration.EasyConfiguration;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.json.JSONConfigProvider;
import config.model.TestModel;
import config.source.DemoConfiguration;
import org.junit.Test;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class JSONConfigTest {
protected final JSONConfigProvider provider = EasyConfiguration.from("target/config.yml", "config.yml");
protected final JSONConfigProvider provider = EasyConfiguration.from("target/config.json", "config.json");
@Test
public void onTest() {
provider.initialize(DemoConfiguration.class);
ConfigurationTest.testDemo(this.provider);
ConfigurationTest.testInner(this.provider);
testDemo();
try {
provider.save();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void testDemo() {
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("----------------------------------------------------");
System.out.println("> Test Value:");
System.out.println("before: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get());
DemoConfiguration.Sub.UUID_CONFIG_VALUE.set(UUID.randomUUID());
System.out.println("after: " + DemoConfiguration.Sub.UUID_CONFIG_VALUE.get());
System.out.println("> Test List:");
DemoConfiguration.Sub.That.OPERATORS.getNotNull().forEach(System.out::println);
List<UUID> operators = IntStream.range(0, 5).mapToObj(i -> UUID.randomUUID()).collect(Collectors.toList());
DemoConfiguration.Sub.That.OPERATORS.set(operators);
System.out.println("> Test Section:");
System.out.println(DemoConfiguration.MODEL_TEST.get());
DemoConfiguration.MODEL_TEST.set(TestModel.random());
System.out.println("> Test Maps:");
DemoConfiguration.USERS.getNotNull().forEach((k, v) -> System.out.println(k + ": " + v));
LinkedHashMap<Integer, UUID> data = new LinkedHashMap<>();
for (int i = 1; i <= 5; i++) {
data.put(i, UUID.randomUUID());
}
DemoConfiguration.USERS.set(data);
System.out.println("----------------------------------------------------");
ConfigurationTest.save(this.provider);
}
@@ -1,39 +0,0 @@
package config.model;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import java.util.HashMap;
import java.util.Map;
public class SomeModel extends AbstractModel {
int num;
public SomeModel(@NotNull String name, int num) {
super(name);
this.num = num;
}
@Override
public String toString() {
return "SomeModel{" +
"name='" + name + '\'' +
", num=" + num +
'}';
}
public @NotNull Map<String, Object> serialize() {
Map<String, Object> map = new HashMap<>();
map.put("name", name);
map.put("num", num);
return map;
}
@TestOnly
public static SomeModel deserialize(Map<String, ?> args) {
return new SomeModel((String) args.get("name"), (Integer) args.get("num"));
}
}
@@ -1,30 +0,0 @@
package config.source;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import cc.carm.lib.configuration.core.annotation.ConfigComment;
import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.value.ConfigValue;
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
@ConfigComment({"数据库配置", " 用于提供数据库连接,进行数据库操作。"})
public class DatabaseConfiguration extends ConfigurationRoot {
@ConfigPath("driver")
protected static final ConfigValue<String> DRIVER_NAME = ConfiguredValue.of(
String.class, "com.mysql.cj.jdbc.Driver"
);
protected static final ConfigValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
protected static final ConfigValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
protected static final ConfigValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
protected static final ConfigValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
protected static final ConfigValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
protected static final ConfigValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
protected static String buildJDBC() {
return String.format("jdbc:mysql://%s:%s/%s%s", HOST.get(), PORT.get(), DATABASE.get(), EXTRA.get());
}
}
@@ -1,65 +0,0 @@
package config.source;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.value.ConfigValue;
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
import config.model.TestModel;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
public class DemoConfiguration extends ConfigurationRoot {
@ConfigPath(root = true)
protected static final ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
// 可以直接写静态内部类,并通过 Class<?> 声明。
public static final Class<?> SUB_TEST = Sub.class;
@ConfigPath("user") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
public static final ConfigValue<TestModel> MODEL_TEST = ConfiguredSection
.builder(TestModel.class)
.defaults(new TestModel("Carm", UUID.randomUUID()))
.parseValue((section, defaultValue) -> TestModel.deserialize(section))
.serializeValue(TestModel::serialize).build();
// 子配置文件
@ConfigPath("database")
public static final Class<?> DB_CONFIG = DatabaseConfiguration.class;
public static final ConfigValue<Map<Integer, UUID>> USERS = ConfiguredMap
.builder(Integer.class, UUID.class).fromString()
.parseKey(Integer::parseInt)
.parseValue(v -> Objects.requireNonNull(UUID.fromString(v)))
.build();
public static class Sub extends ConfigurationRoot {
@ConfigPath(value = "uuid-value", root = true)
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
.builder(UUID.class).fromString()
.parseValue((data, defaultValue) -> UUID.fromString(data))
.build();
public static final Class<?> NOTHING = That.class;
public static class That extends ConfigurationRoot {
public static final ConfigValue<List<UUID>> OPERATORS = ConfiguredList
.builder(UUID.class).fromString()
.parseValue(s -> Objects.requireNonNull(UUID.fromString(s)))
.build();
}
}
}
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>3.3.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<artifactId>easyconfiguration-sql</artifactId>
<dependencies>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-core</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,6 @@
package cc.carm.lib.configuration;
public class EasyConfiguration {
}
@@ -0,0 +1,38 @@
package cc.carm.lib.configuration.sql;
import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.core.source.ConfigurationComments;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SQLConfigProvider extends ConfigurationProvider<SQLSectionWrapper> {
@Override
public @NotNull SQLSectionWrapper getConfiguration() {
return null;
}
@Override
public void save() throws Exception {
}
@Override
protected void onReload() throws Exception {
}
@Override
public @Nullable ConfigurationComments getComments() {
return null;
}
@Override
public @NotNull ConfigInitializer<? extends ConfigurationProvider<SQLSectionWrapper>> getInitializer() {
return null;
}
}
@@ -0,0 +1,63 @@
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;
}
}
@@ -0,0 +1,7 @@
package cc.carm.lib.configuration.sql;
public class SQLValueParser {
}
+14
View File
@@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS conf
(
`namespace` VARCHAR(255) NOT NULL, #
`section` VARCHAR(255) NOT NULL, # (ConfigPath)
`type` VARCHAR(255) NOT NULL, # (Integer/Byte/List/Map/...)
`value` MEDIUMTEXT, # (JSON格式)
`inline_comments` TINYTEXT, #
`header_comments` TEXT, #
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`namespace`, `section`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;
+13 -4
View File
@@ -5,13 +5,15 @@
<parent>
<artifactId>easyconfiguration-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>2.2.0</version>
<version>3.3.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<artifactId>easyconfiguration-yaml</artifactId>
@@ -27,9 +29,16 @@
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>yamlconfiguration-commented</artifactId>
<version>2.0.2</version>
<groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-demo</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bspfsystems</groupId>
<artifactId>yamlconfiguration</artifactId>
<version>1.3.0</version>
<scope>compile</scope>
</dependency>
@@ -30,5 +30,4 @@ public class EasyConfiguration {
return from(new File(fileName), source);
}
}
@@ -1,35 +1,111 @@
package cc.carm.lib.configuration.yaml;
import org.bspfsystems.yamlconfiguration.commented.CommentsProvider;
import cc.carm.lib.configuration.core.source.ConfigurationComments;
import org.bspfsystems.yamlconfiguration.configuration.ConfigurationSection;
import org.bspfsystems.yamlconfiguration.file.FileConfiguration;
import org.bspfsystems.yamlconfiguration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class YAMLComments implements CommentsProvider {
import static cc.carm.lib.configuration.yaml.YAMLConfigProvider.SEPARATOR;
Map<String, String[]> comments = new HashMap<>();
@SuppressWarnings("SpellCheckingInspection")
public class YAMLComments extends ConfigurationComments {
protected Map<String, String[]> getComments() {
return comments;
}
public @Nullable String buildHeaderComments(@Nullable String path, @NotNull String indents) {
List<String> comments = getHeaderComment(path);
if (comments == null || comments.size() == 0) return null;
public void set(@NotNull String path, @NotNull String... comments) {
if (comments.length == 0) {
getComments().remove(path);
} else {
getComments().put(path, comments);
StringJoiner joiner = new StringJoiner("\n");
for (String comment : comments) {
if (comment.length() == 0) joiner.add(" ");
else joiner.add(indents + "# " + comment);
}
return joiner + "\n";
}
public @Nullable String[] get(@NotNull String path) {
return getComments().get(path);
/**
* 从一个文件读取配置并写入注释到某个写入器中。
* 该方法的部分源代码借鉴自 tchristofferson/ConfigUpdater 项目。
*
* @param source 源配置文件
* @param writer 配置写入器
* @throws IOException 当写入发生错误时抛出
*/
public void writeComments(@NotNull YamlConfiguration source, @NotNull BufferedWriter writer) throws IOException {
FileConfiguration temp = new YamlConfiguration(); // 该对象用于临时记录配置内容
String configHeader = buildHeaderComments(null, "");
if (configHeader != null) writer.write(configHeader);
for (String fullKey : source.getKeys(true)) {
Object currentValue = source.get(fullKey);
String indents = getIndents(fullKey);
String headerComments = buildHeaderComments(fullKey, indents);
String inlineComment = getInlineComment(fullKey);
if (headerComments != null) writer.write(headerComments);
String[] splitFullKey = fullKey.split("[" + SEPARATOR + "]");
String trailingKey = splitFullKey[splitFullKey.length - 1];
if (currentValue instanceof ConfigurationSection) {
ConfigurationSection section = (ConfigurationSection) currentValue;
writer.write(indents + trailingKey + ":");
if (inlineComment != null && inlineComment.length() > 0) {
writer.write(" # " + inlineComment);
}
if (!section.getKeys(false).isEmpty()) {
writer.write("\n");
} else {
writer.write(" {}\n");
if (indents.length() == 0) writer.write("\n");
}
continue;
}
temp.set(trailingKey, currentValue);
String yaml = temp.saveToString();
temp.set(trailingKey, null);
yaml = yaml.substring(0, yaml.length() - 1);
if (inlineComment != null && inlineComment.length() > 0) {
if (yaml.contains("\n")) {
// section为多行内容,需要 InlineComment 加在首行末尾
String[] splitLine = yaml.split("\n", 2);
yaml = splitLine[0] + " # " + inlineComment + "\n" + splitLine[1];
} else {
// 其他情况下就直接加载后面就好。
yaml += " # " + inlineComment;
}
}
writer.write(indents + yaml.replace("\n", "\n" + indents) + "\n");
if (indents.length() == 0) writer.write("\n");
}
writer.close();
}
@Override
public String[] apply(String s) {
return get(s);
/**
* 得到一个键的缩进。
* 该方法的源代码来自 tchristofferson/ConfigUpdater 项目。
*
* @param key 键
* @return 该键的缩进文本
*/
protected static String getIndents(String key) {
String[] splitKey = key.split("[" + YAMLConfigProvider.SEPARATOR + "]");
return IntStream.range(1, splitKey.length).mapToObj(i -> " ").collect(Collectors.joining());
}
}
@@ -1,17 +1,25 @@
package cc.carm.lib.configuration.yaml;
import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.core.source.ConfigurationComments;
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
import org.bspfsystems.yamlconfiguration.commented.CommentedYamlConfiguration;
import org.bspfsystems.yamlconfiguration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedWriter;
import java.io.File;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public class YAMLConfigProvider extends FileConfigProvider<YAMLSectionWrapper> {
protected static final char SEPARATOR = '.';
protected final @NotNull YAMLComments comments = new YAMLComments();
protected CommentedYamlConfiguration configuration;
protected YamlConfiguration configuration;
protected ConfigInitializer<YAMLConfigProvider> initializer;
public YAMLConfigProvider(@NotNull File file) {
@@ -19,7 +27,7 @@ public class YAMLConfigProvider extends FileConfigProvider<YAMLSectionWrapper> {
}
public void initializeConfig() {
this.configuration = CommentedYamlConfiguration.loadConfiguration(comments, file);
this.configuration = YamlConfiguration.loadConfiguration(file);
this.initializer = new ConfigInitializer<>(this);
}
@@ -29,23 +37,32 @@ public class YAMLConfigProvider extends FileConfigProvider<YAMLSectionWrapper> {
}
@Override
public void reload() throws Exception {
protected void onReload() throws Exception {
configuration.load(getFile());
}
@Override
public @Nullable ConfigurationComments getComments() {
return this.comments;
}
@Override
@SuppressWarnings("SpellCheckingInspection")
public void save() throws Exception {
configuration.save(getFile());
}
@Override
public void setComments(@NotNull String path, @NotNull String... comments) {
this.comments.set(path, comments);
}
// tchristofferson/ConfigUpdater start
StringWriter writer = new StringWriter();
this.comments.writeComments(configuration, new BufferedWriter(writer));
String value = writer.toString(); // config contents
Path toUpdatePath = getFile().toPath();
if (!value.equals(new String(Files.readAllBytes(toUpdatePath), StandardCharsets.UTF_8))) {
// if updated contents are not the same as current file contents, update
Files.write(toUpdatePath, value.getBytes(StandardCharsets.UTF_8));
}
// tchristofferson/ConfigUpdater end
@Override
public @Nullable String[] getComments(@NotNull String path) {
return this.comments.get(path);
}
@Override
@@ -12,7 +12,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
public class YAMLSectionWrapper implements ConfigurationWrapper {
public class YAMLSectionWrapper implements ConfigurationWrapper<ConfigurationSection> {
private final ConfigurationSection section;
@@ -25,6 +25,11 @@ public class YAMLSectionWrapper implements ConfigurationWrapper {
return section == null ? null : new YAMLSectionWrapper(section);
}
@Override
public @NotNull ConfigurationSection getSource() {
return this.section;
}
@Override
public @NotNull Set<String> getKeys(boolean deep) {
return new LinkedHashSet<>(section.getKeys(deep));
@@ -66,7 +71,7 @@ public class YAMLSectionWrapper implements ConfigurationWrapper {
}
@Override
public @Nullable ConfigurationWrapper getConfigurationSection(@NotNull String path) {
public @Nullable YAMLSectionWrapper getConfigurationSection(@NotNull String path) {
return of(this.section.getConfigurationSection(path));
}
@@ -6,15 +6,18 @@ import cc.carm.lib.configuration.yaml.builder.YAMLConfigBuilder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public abstract class YAMLValue<T> extends CachedConfigValue<T> {
public static @NotNull YAMLConfigBuilder builder() {
return new YAMLConfigBuilder();
}
public YAMLValue(@Nullable YAMLConfigProvider provider,
@Nullable String configPath, @NotNull String[] comments, @Nullable T defaultValue) {
super(provider, configPath, comments, defaultValue);
public YAMLValue(@Nullable YAMLConfigProvider provider, @Nullable String configPath,
@Nullable List<String> headerComments, @Nullable String inlineComments,
@Nullable T defaultValue) {
super(provider, configPath, headerComments, inlineComments, defaultValue);
}
public YAMLConfigProvider getYAMLProvider() {
@@ -21,7 +21,7 @@ public class SerializableBuilder<T extends ConfigurationSerializable>
@Override
public @NotNull ConfiguredSerializable<T> build() {
return new ConfiguredSerializable<>(this.provider, this.path, this.comments, this.valueClass, this.defaultValue);
return new ConfiguredSerializable<>(this.provider, this.path, this.headerComments, this.inlineComment, this.valueClass, this.defaultValue);
}
@@ -1,11 +1,12 @@
package cc.carm.lib.configuration.yaml.value;
import cc.carm.lib.configuration.yaml.YAMLValue;
import cc.carm.lib.configuration.yaml.YAMLConfigProvider;
import cc.carm.lib.configuration.yaml.YAMLValue;
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Optional;
public class ConfiguredSerializable<T extends ConfigurationSerializable> extends YAMLValue<T> {
@@ -15,16 +16,16 @@ public class ConfiguredSerializable<T extends ConfigurationSerializable> extends
}
public static <V extends ConfigurationSerializable> ConfiguredSerializable<V> of(@NotNull Class<V> valueClass,
@Nullable V defaultValue) {
@Nullable V defaultValue) {
return builder().ofSerializable(valueClass).defaults(defaultValue).build();
}
protected final @NotNull Class<T> valueClass;
public ConfiguredSerializable(@Nullable YAMLConfigProvider provider,
@Nullable String configPath, @NotNull String[] comments,
public ConfiguredSerializable(@Nullable YAMLConfigProvider provider, @Nullable String configPath,
@Nullable List<String> headerComments, @Nullable String inlineComments,
@NotNull Class<T> valueClass, @Nullable T defaultValue) {
super(provider, configPath, comments, defaultValue);
super(provider, configPath, headerComments, inlineComments, defaultValue);
this.valueClass = valueClass;
}
@@ -0,0 +1,47 @@
package config;
import cc.carm.lib.configuration.EasyConfiguration;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.demo.tests.model.AbstractModel;
import cc.carm.lib.configuration.yaml.YAMLConfigProvider;
import config.model.AnyModel;
import config.model.SomeModel;
import config.source.ModelConfiguration;
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerialization;
import org.junit.Test;
public class DemoConfigTest {
static {
ConfigurationSerialization.registerClass(SomeModel.class);
ConfigurationSerialization.registerClass(AnyModel.class);
}
protected final YAMLConfigProvider provider = EasyConfiguration.from("target/config.yml", "config.yml");
@Test
public void onTest() {
ConfigurationTest.testDemo(this.provider);
ConfigurationTest.testInner(this.provider);
testSerialization(this.provider);
ConfigurationTest.save(this.provider);
}
public static void testSerialization(YAMLConfigProvider provider) {
provider.initialize(ModelConfiguration.class);
System.out.println("----------------------------------------------------");
AbstractModel someModel = ModelConfiguration.SOME_MODEL.get();
if (someModel != null) System.out.println(someModel.getName());
AbstractModel anyModel = ModelConfiguration.ANY_MODEL.get();
if (anyModel != null) System.out.println(anyModel.getName());
System.out.println("----------------------------------------------------");
}
}
@@ -1,26 +0,0 @@
package config;
import config.offset.FieldOffset;
import config.offset.OffsetUtil;
import config.source.DemoConfiguration;
import org.junit.Test;
import java.util.List;
public class OffsetTest {
@Test
public void test() {
//
// output(OffsetUtil.getClassMemberOffset(DemoConfiguration.class));
// output(OffsetUtil.getClassMemberOffset(DemoConfiguration.Sub.class));
}
protected static void output(List<FieldOffset> fieldOffsets) {
for (FieldOffset fieldOffset : fieldOffsets) {
System.out.println(fieldOffset.getOffsetValue() + " -> " + fieldOffset);
}
}
}
@@ -1,17 +0,0 @@
package config.model;
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
import org.jetbrains.annotations.NotNull;
public abstract class AbstractModel implements ConfigurationSerializable {
protected final @NotNull String name;
public AbstractModel(@NotNull String name) {
this.name = name;
}
public @NotNull String getName() {
return name;
}
}
@@ -0,0 +1,50 @@
package config.model;
import cc.carm.lib.configuration.demo.tests.model.AbstractModel;
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
import org.bspfsystems.yamlconfiguration.serialization.SerializableAs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@SerializableAs("AnyModel")
public class AnyModel extends AbstractModel implements ConfigurationSerializable {
public final boolean bool;
public AnyModel(@NotNull String name, boolean bool) {
super(name);
this.bool = bool;
}
@Override
public String toString() {
return "AnyModel{" +
"name='" + name + '\'' +
", bool=" + bool +
'}';
}
@Override
public @NotNull Map<String, Object> serialize() {
Map<String, Object> map = new HashMap<>();
map.put("name", name);
map.put("state", bool);
return map;
}
public static AnyModel random() {
return new AnyModel(UUID.randomUUID().toString().substring(0, 5), Math.random() > 0.5);
}
@TestOnly
public static AnyModel deserialize(Map<String, ?> args) {
return new AnyModel((String) args.get("name"), (Boolean) args.get("state"));
}
}
@@ -1,5 +1,6 @@
package config.model;
import cc.carm.lib.configuration.demo.tests.model.AbstractModel;
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
import org.bspfsystems.yamlconfiguration.serialization.SerializableAs;
import org.jetbrains.annotations.NotNull;
@@ -7,11 +8,12 @@ import org.jetbrains.annotations.TestOnly;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@SerializableAs("SomeModel")
public class SomeModel extends AbstractModel implements ConfigurationSerializable {
int num;
public final int num;
public SomeModel(@NotNull String name, int num) {
super(name);
@@ -34,6 +36,11 @@ public class SomeModel extends AbstractModel implements ConfigurationSerializabl
return map;
}
public static SomeModel random() {
return new SomeModel(UUID.randomUUID().toString().substring(0, 5), (int) (Math.random() * 1000));
}
@TestOnly
public static SomeModel deserialize(Map<String, ?> args) {
return new SomeModel((String) args.get("name"), (Integer) args.get("num"));
@@ -1,72 +0,0 @@
package config.model;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerializable;
import org.bspfsystems.yamlconfiguration.serialization.SerializableAs;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.TestOnly;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@SerializableAs("TestModel")
public class TestModel extends AbstractModel implements ConfigurationSerializable {
public UUID uuid;
public TestModel(String name, UUID uuid) {
super(name);
this.uuid = uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public UUID getUuid() {
return uuid;
}
@Override
public @NotNull Map<String, Object> serialize() {
Map<String, Object> map = new HashMap<>();
map.put("name", name);
Map<String, Object> map2 = new HashMap<>();
map2.put("uuid", uuid.toString());
map.put("info", map2);
return map;
}
public static TestModel deserialize(ConfigurationWrapper section) {
String name = section.getString("name");
if (name == null) throw new NullPointerException("name is null");
String uuidString = section.getString("info.uuid");
if (uuidString == null) throw new NullPointerException("uuid is null");
return new TestModel(name, UUID.fromString(uuidString));
}
@TestOnly
@SuppressWarnings("unchecked")
public static TestModel deserialize(Map<String, ?> args) {
String name = (String) args.get("name");
if (name == null) throw new NullPointerException("name is null");
Map<String, ?> map = (Map<String, ?>) args.get("info");
String uuidString = (String) map.get("uuid");
if (uuidString == null) throw new NullPointerException("uuid is null");
return new TestModel(name, UUID.fromString(uuidString));
}
public static TestModel random() {
return new TestModel(UUID.randomUUID().toString().substring(0, 5), UUID.randomUUID());
}
@Override
public String toString() {
return "TestUser{" +
"name='" + name + '\'' +
", uuid=" + uuid +
'}';
}
}
@@ -1,63 +0,0 @@
package config.offset;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* @author Chris2018998
*/
public class OffsetUtil {
private static Unsafe unsafe;
static {
try {
// 获取 Unsafe 内部的私有的实例化单例对象
Field field = Unsafe.class.getDeclaredField("theUnsafe");
// 无视权限
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
// try {
// unsafe = AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> {
// Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
// theUnsafe.setAccessible(true);
// return (Unsafe) theUnsafe.get(null);
// });
// } catch (Throwable e) {
// System.err.println("Unable to load unsafe");
// }
}
public static List<FieldOffset> getClassMemberOffset(Class<?> beanClass) {
List<FieldOffset> offsetsList = new LinkedList<>();
for (Field field : beanClass.getDeclaredFields()) {
FieldOffset fieldOffset = new FieldOffset(field);
offsetsList.add(fieldOffset);
if (Modifier.isStatic(field.getModifiers()))
fieldOffset.setOffsetValue(unsafe.staticFieldOffset(field));
else
fieldOffset.setOffsetValue(unsafe.objectFieldOffset(field));
Class<?> fieldType = field.getType();
if (!fieldType.getName().startsWith("java")) {
Field[] subfields = fieldType.getDeclaredFields();
if (subfields.length > 0) {
fieldOffset.setSubFieldOffsetList(getClassMemberOffset(fieldType));
}
}
}
Collections.sort(offsetsList);
return offsetsList;
}
}
@@ -1,16 +0,0 @@
package config.source;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.value.ConfigValue;
import cc.carm.lib.configuration.yaml.value.ConfiguredSerializable;
import config.model.AbstractModel;
import config.model.TestModel;
@ConfigPath(root = true)
public class ImplConfiguration extends ConfigurationRoot {
public static final ConfigValue<? extends AbstractModel> TEST = ConfiguredSerializable.of(TestModel.class, TestModel.random());
}
@@ -0,0 +1,22 @@
package config.source;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import cc.carm.lib.configuration.core.annotation.ConfigPath;
import cc.carm.lib.configuration.core.value.ConfigValue;
import cc.carm.lib.configuration.demo.tests.model.AbstractModel;
import cc.carm.lib.configuration.yaml.value.ConfiguredSerializable;
import config.model.AnyModel;
import config.model.SomeModel;
@ConfigPath("model-test")
public class ModelConfiguration extends ConfigurationRoot {
public static final ConfigValue<? extends AbstractModel> SOME_MODEL = ConfiguredSerializable.of(
SomeModel.class, SomeModel.random()
);
public static final ConfigValue<? extends AbstractModel> ANY_MODEL = ConfiguredSerializable.of(
AnyModel.class, AnyModel.random()
);
}
+7 -5
View File
@@ -15,11 +15,13 @@
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId>
<packaging>pom</packaging>
<version>2.2.0</version>
<version>3.3.0</version>
<modules>
<module>core</module>
<module>impl/yaml</module>
<module>impl/json</module>
<module>impl/sql</module>
<module>demo</module>
</modules>
<name>EasyConfiguration</name>
@@ -90,7 +92,7 @@
<downloadUrl>https://github.com/CarmJos/EasyConfiguration/releases</downloadUrl>
<site>
<id>javadoc</id>
<name>EasyConfiguration JavaDoc (on Github Pages)</name>
<name>EasyConfiguration JavaDoc (on GitHub Pages)</name>
<url>https://CarmJos.github.io/EasyConfiguration</url>
</site>
</distributionManagement>
@@ -164,7 +166,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.0</version>
<version>3.4.1</version>
<configuration>
<classifier>javadoc</classifier>
<detectJavaApiLink>false</detectJavaApiLink>
@@ -203,7 +205,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
<version>3.3.0</version>
</plugin>
<plugin>
@@ -223,7 +225,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>