1
mirror of https://github.com/CarmJos/EasyConfiguration.git synced 2024-09-19 12:15:52 +00:00

初始版本完成

This commit is contained in:
Carm Jos 2022-04-17 00:32:33 +08:00
commit 3d0927aeeb
62 changed files with 5235 additions and 0 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
github: [ CarmJos ]
custom: [ 'https://donate.carm.cc' ]

37
.github/ISSUE_TEMPLATE/bugs_report.md vendored Normal file
View File

@ -0,0 +1,37 @@
---
name: 问题提交
about: 描述问题并提交,帮助我们对其进行检查与修复。
title: ''
labels: bug
assignees: ''
---
### **问题简述**
用简短的话语描述一下大概问题。
### **问题来源**
描述一下通过哪些操作才发现的问题,如:
1. 使用了 '...'
2. 输入了 '....'
3. 出现了报错 '....'
### **预期结果** (可选)
如果问题不发生,应该是什么情况
### **问题截图/问题报错**
如果有报错或输出,请提供截图。
### **操作环境**
- 系统环境: `Windows 10` / `Ubuntu` / `...`
- Java版本: `JDK11` / `OPENJDK8` / `JRE8` / `...`
### **其他补充**
如有其他补充,可以在这里描述。

View File

@ -0,0 +1,23 @@
---
name: 功能需求
about: 希望我们提供更多的功能。
title: ''
labels: enhancement
assignees: ''
---
### **功能简述**
简单的描述一下你想要的功能
### **需求来源**
简单的描述一下为什么需要这个功能。
### **功能参考**(可选)
如果有相关功能的参考,如文本、截图,请提供给我们。
### **附加内容**
如果有什么小细节需要重点注意,请在这里告诉我们。

11
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "maven" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"

41
.github/gpg_public.key vendored Normal file
View File

@ -0,0 +1,41 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQGNBGHwDt0BDAC+2u7hHXIp+C3tvUc5w7Ga5gDVNN3xTQEurGXgYSnGnNPb89h/
tk6MBQ2AHdsj61yK/mH65RbDZe725+0zBvumxfrPbgqYBy9veE1Cjpl3wJwsGYa+
gidq3tU2WBpUpaFOcyfxzvoDjKv6BClX+m7RijRM4tTSxmzrUTfwrClSdSV2HlBu
AuKvY5W+cDwlKtuXEBtgCpdlOGsp8YZsqe4QD9xMI6GOOnXnHisYnmsMzn2RU8mW
GUS3ob1J1vAfIinixwB8tHlxB/G3jaOXtQEwFmI2dfYOdkbxOiIgcSfbRI8PGiHA
KiluZpn+Ww05GwUch2HdX8dw1hsbWM4G/X8Aqy3HdJB28p73dE4I9FRrJ1uxsmMe
iON8QevhSBC0qwSxb+16vKt58ErQnqXrJI6+HzPldn22OQIF7bMZGwYkZiOjS5LU
xAoRT4Jomks0ccOZGe7wMIUp2Ch22vmv4O78Pd2GEzAcTUvM8mrS+zJBMogjx27C
r86HOWEjmi2R32EAEQEAAbQeQ2FybSBKb3MgPEthcm11bkpAb3V0bG9vay5jb20+
iQHUBBMBCAA+FiEEL6NL2WG27xbAlAIkh337tzeYbfcFAmHwDt0CGwMFCQPCZwAF
CwkIBwIGFQoJCAsCBBYCAwECHgECF4AACgkQh337tzeYbffNvQwAscXykUimCOli
lRK52P6+w5n/arl7UxCh7TZiRjf9feiCp3OivETKCeqnbtNTgv67aNbxjO9asCTK
dU6J6Zh6wO8CqDhg+EA8qn+Nu4ESPGvgyWyeck9otMy16To5/I9eQRYTOos1crOA
DRUH1MWLeIkZabM6wSPad/CcRAzFNf5+8JNuQqCgQ3Rngst1Z6Gyb1hixWnjxc4P
7dFquwbR0D0ojwj0Etqd0c5p0iwyRl2I2QQ1bS3aGqdW0LzM9ixh25HAReg2QH7G
FBQ5PLLXr4UqYQygzwhUtxl2jra0+3ia+D7OBwlgm3QPnlo82Z7nExQUYmemD7jV
3Gc1ELXKSRHKbVjSoGiHWpnSiw4ptLo+tnzhRCHlV+pTS3IbQoPdb/glBOVIkA/j
ksCfbrmC8aXpk1YycAXY2my7BpXsImWAOwPHVsvcB2IpEA2s3VfsZ/IB9z+yih3n
z8mL0BFjKWUV23IOoeRqmt7l8nB7u55Nbjasu0LdTcl2R6swE3fTuQGNBGHwDt0B
DAChLPfZ1njctL8BijLO//Hgvw9E6STJGYgqglNetfdoir+YAwCPQ32K4MsaQKl8
xQelmcOU+5jO2C8wEyNAjmvyKGB2J/IjLEtAlbOn1UltKQ/GhxgMjg0EheY81ZMa
7FDq1TDwYRCN5SMKhl5GF0JJ4OWfg1i7HbpEfkw4mW1pl0/eNdeQaC6qV6EWTsqz
WRbi8DeH1WarSgq/00Za6zxNntLNLoq7jsTbDwTc6pgOp1Z8EcGfI/mcn3moqTxc
o/PLYg+6impCKXVeRUlgGBpJ5YVvR5ACTLS9Tztwho9MpKJ9obXAfwXKyoToHCII
+pTnuzweOfOsrjLsFySnXq8WO2PY9JbNWjveKfk35fGfsrbwU0Vg+m67UahXqA4i
KNvZeA8bG8AXrxUirKLWIj/8AuW8NAKu7ui4YmexldraYUgaoBrqhXZCVe8dNQv+
erzNbmJUCPDauNddnDsCqOoZ8fWyBenDs3NS0TWuvua4/ND+AyVxPeatI4qfS2TD
gnUAEQEAAYkBvAQYAQgAJhYhBC+jS9lhtu8WwJQCJId9+7c3mG33BQJh8A7dAhsM
BQkDwmcAAAoJEId9+7c3mG33znkL/01lWSQOzFd+omzrz0RPqFUksxqQS+CUty0m
/4n9H/K3BLcut+nUNbosNuqPqISoiaV7BGigv0bT+Pu+EQQtyjYOSeibeBadB48w
cYp8k3YJbfinuKApw1Zp9IfAd3eXXWi30OY4FmlsKy6LGnusZ6KS+FzTjU94yN/0
LK05fmBtLN/MQJQyqYIkquzk//diwpsxnv34+10igYaQBAEpPIsmsYwWg+ecCtyx
lJGvmQggBrKvo5EdOGhO9DJAu1WQcFqnUCj5qvL+YKIsMyIwujQH8554P8xfCLFU
a351qs30yWXX4HGMn3o7RuVQAACs1buxlMen/JEdQOLOaUtFcu2iYzCFhuzDsetc
geNinFyo0bV9dXiahG95oTL45OA0w+E9Y0B5VXc9Yf08Yyj8ayMChASfVG5lZU6l
KhiaKHV9t4xmwP43lRjs8HTC5rtXc31kPtOAT61HG9vPA49ZdXybUqoHru15PFmc
OK7d0W/LdJ3iFeselROADHgPQn14sg==
=rRA5
-----END PGP PUBLIC KEY BLOCK-----

54
.github/workflows/codacy-analysis.yml vendored Normal file
View File

@ -0,0 +1,54 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow checks out code, performs a Codacy security scan
# and integrates the results with the
# GitHub Advanced Security code scanning feature. For more information on
# the Codacy security scan action usage and parameters, see
# https://github.com/codacy/codacy-analysis-cli-action.
# For more information on Codacy Analysis CLI in general, see
# https://github.com/codacy/codacy-analysis-cli.
name: "Codacy Security Scan"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '27 16 * * 5'
jobs:
codacy-security-scan:
name: Codacy Security Scan
runs-on: ubuntu-latest
steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout code
uses: actions/checkout@v2
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@d840f886c4bd4edc059706d09c6a1586111c540b
with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
verbose: true
output: results.sarif
format: sarif
# Adjust severity of non-security issues
gh-code-scanning-compat: true
# Force 0 exit code to allow SARIF file generation
# This will handover control about PR rejection to the GitHub side
max-allowed-issues: 2147483647
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
uses: github/codeql-action/upload-sarif@v1
with:
sarif_file: results.sarif

70
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL Analysis"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '45 12 * * 1'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

108
.github/workflows/deploy.yml vendored Normal file
View File

@ -0,0 +1,108 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: "Project Deploy"
on:
# 支持手动触发构建
workflow_dispatch:
release:
# 创建release的时候触发
types: [ published ]
jobs:
gh-deploy:
name: "Publish Project (GitHub)"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: "Set up JDK"
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
cache: maven
server-id: github
server-username: MAVEN_USERNAME
server-password: MAVEN_TOKEN
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
- name: "Maven Deploy With Javadoc"
run: mvn -B -Pgithub deploy --file pom.xml -DskipTests
env:
MAVEN_USERNAME: ${{ github.repository_owner }}
MAVEN_TOKEN: ${{secrets.GITHUB_TOKEN}}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
- name: "Copy Javadoc to Location"
run: |
rm -rf docs
mkdir -vp docs
cp -vrf api/target/apidocs/* docs/
cp -vrf .documentation/JAVADOC-README.md docs/README.md
- name: "Generate the Javadoc sitemap"
id: sitemap
uses: cicirello/generate-sitemap@v1
with:
base-url-path: https://carmjos.github.io/EasySQL
path-to-root: docs
- name: "Output stats"
run: |
echo "sitemap-path = ${{ steps.sitemap.outputs.sitemap-path }}"
echo "url-count = ${{ steps.sitemap.outputs.url-count }}"
echo "excluded-count = ${{ steps.sitemap.outputs.excluded-count }}"
- name: "Configure Git"
env:
DEPLOY_PRI: ${{secrets.DEPLOY_PRI}}
run: |
sudo timedatectl set-timezone "Asia/Shanghai"
mkdir -p ~/.ssh/
echo "$DEPLOY_PRI" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
ssh-keyscan github.com >> ~/.ssh/known_hosts
git config --global user.name 'CarmJos'
git config --global user.email 'carm@carm.cc'
- name: "Commit documentations"
run: |
cd docs
git init
git remote add origin git@github.com:CarmJos/EasySQL.git
git checkout -b gh-pages
git add -A
git commit -m "API Document generated."
- name: "Push javadocs"
run: |
cd docs
git push origin HEAD:gh-pages --force
central-deploy:
name: "Deploy Project (Central Repository)"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: "Set up JDK"
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
cache: maven
server-id: ossrh
server-username: MAVEN_USERNAME
server-password: MAVEN_PASSWORD
gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import
gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase
- name: "Central Deploy"
run: mvn -B -Possrh deploy --file pom.xml -DskipTests
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USER }}
MAVEN_PASSWORD: ${{ secrets.OSSRH_PASS }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}

32
.github/workflows/maven.yml vendored Normal file
View File

@ -0,0 +1,32 @@
# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Project Build & Tests
on:
# 支持手动触发构建
workflow_dispatch:
pull_request:
push:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: "Set up JDK"
uses: actions/setup-java@v2
with:
java-version: '11'
distribution: 'adopt'
- name: "Package"
run: mvn -B package --file pom.xml -Dgpg.skip
- name: "Target Stage"
run: mkdir staging && cp */target/*.jar staging
- name: "Upload artifact"
uses: actions/upload-artifact@v2
with:
name: Artifact
path: staging

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/.idea/
**/target/
**.iml

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Carm
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

229
README.md Normal file
View File

@ -0,0 +1,229 @@
```text
____ _____ ____ __ _
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
/___/ /___/
```
# 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)
[![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/easysql/badge)](https://www.codefactor.io/repository/github/carmjos/easysql)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/EasyConfiguration)
![](https://visitor-badge.glitch.me/badge?page_id=EasyConfiguration.readme)
轻松(做)配置,简单便捷的通用配置文件加载、读取与更新工具,可自定义配置格式。
## 优势
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
## 开发
详细开发介绍请 [点击这里](.documentation/README.md) , JavaDoc(最新Release) 请 [点击这里](https://carmjos.github.io/EasyConfiguration) 。
### 示例代码
您可以 [点击这里](impl/yaml/src/test/java/config/source/TestConfiguration.java) 查看部分代码演示,更多演示详见 [开发介绍](.documentation/README.md) 。
### 依赖方式
#### Maven 依赖
<details>
<summary>远程库配置</summary>
```xml
<project>
<repositories>
<repository>
<!--采用Maven中心库安全稳定但版本更新需要等待同步-->
<id>maven</id>
<name>Maven Central</name>
<url>https://repo1.maven.org/maven2</url>
</repository>
<repository>
<!--采用github依赖库实时更新但需要配置 (推荐) -->
<id>EasyConfiguration</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
</repository>
<repository>
<!--采用我的私人依赖库,简单方便,但可能因为变故而无法使用-->
<id>carm-repo</id>
<name>Carm's Repo</name>
<url>https://repo.carm.cc/repository/maven-public/</url>
</repository>
</repositories>
</project>
```
</details>
<details>
<summary>通用原生依赖</summary>
```xml
<project>
<dependencies>
<!--基础实现部分需要自行实现“Provider”与“Wrapper”。-->
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-core</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
<!--基于YAML文件的实现版本可用于全部Java环境。-->
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-yaml</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
```
</details>
<details>
<summary>平台依赖版本</summary>
<details>
<summary>基于Spigot实现的版本</summary>
```xml
<project>
<dependencies>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-spigot</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
```
</details>
<details>
<summary>基于Bungee实现的版本 (不支持自动注释)</summary>
```xml
<project>
<dependencies>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-bungee</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
```
</details>
</details>
#### Gradle 依赖
<details>
<summary>远程库配置</summary>
```groovy
repositories {
// 采用Maven中心库安全稳定但版本更新需要等待同步
mavenCentral()
// 采用github依赖库实时更新但需要配置 (推荐)
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
maven { url 'https://repo.carm.cc/repository/maven-public/' }
}
```
</details>
<details>
<summary>原生依赖</summary>
```groovy
dependencies {
//基础实现部分需要自行实现“Provider”与“Wrapper”。
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
//基于YAML文件的实现版本可用于全部Java环境。
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
}
```
</details>
<details>
<summary>含连接池版本</summary>
```groovy
dependencies {
// 用于Spigot服务端的版本
api "cc.carm.lib:easyconfiguration-spigot:[LATEST RELEASE]"
// 用于BungeeCord服务端的版本不支持注解。
// 如需注解,可选择使用 `easyconfiguration-yaml` 并打包。
api "cc.carm.lib:easyconfiguration-bungee:[LATEST RELEASE]"
}
```
</details>
## 支持与捐赠
若您觉得本插件做的不错,您可以通过捐赠支持我!
感谢您对开源项目的支持!
<img height=25% width=25% src="https://raw.githubusercontent.com/CarmJos/CarmJos/main/img/donate-code.jpg" alt=""/>
## 开源协议
本项目源码采用 [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>

19
core/pom.xml Normal file
View File

@ -0,0 +1,19 @@
<?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>1.0.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>
</properties>
<artifactId>easyconfiguration-core</artifactId>
<packaging>jar</packaging>
</project>

View File

@ -0,0 +1,100 @@
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.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Optional;
public class ConfigInitializer {
public static void initialize(ConfigurationProvider source, Class<? extends ConfigurationRoot> rootClazz) {
initialize(source, rootClazz, true);
}
public static void initialize(ConfigurationProvider provider, Class<? extends ConfigurationRoot> rootClazz, boolean saveDefault) {
ConfigPath sectionAnnotation = rootClazz.getAnnotation(ConfigPath.class);
String rootSection = null;
if (sectionAnnotation != null && sectionAnnotation.value().length() > 1) {
rootSection = sectionAnnotation.value();
}
for (Class<?> innerClass : rootClazz.getDeclaredClasses()) {
initSection(provider, rootSection, innerClass, saveDefault);
}
for (Field field : rootClazz.getFields()) {
initValue(provider, rootSection, rootClazz, field, saveDefault);
}
if (saveDefault) {
try {
provider.save();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void initSection(ConfigurationProvider provider, String parentSection, Class<?> clazz, boolean saveDefault) {
if (!Modifier.isStatic(clazz.getModifiers()) || !Modifier.isPublic(clazz.getModifiers())) return;
String section = getSectionPath(clazz.getSimpleName(), parentSection, clazz.getAnnotation(ConfigPath.class));
ConfigComment comments = clazz.getAnnotation(ConfigComment.class);
if (comments != null && comments.value().length > 0) {
provider.setComments(parentSection, comments.value());
}
for (Field field : clazz.getFields()) initValue(provider, section, clazz, field, saveDefault);
for (Class<?> innerClass : clazz.getDeclaredClasses()) initSection(provider, section, innerClass, saveDefault);
}
private static void initValue(ConfigurationProvider provider, String parentSection, Class<?> clazz, Field field, boolean saveDefault) {
try {
Object object = field.get(clazz);
if (object instanceof ConfigValue<?>) {
initializeValue(
provider, (ConfigValue<?>) object,
getSectionPath(field.getName(), parentSection, field.getAnnotation(ConfigPath.class)),
Optional.ofNullable(field.getAnnotation(ConfigComment.class))
.map(ConfigComment::value).orElse(new String[0]),
saveDefault);
}
} catch (IllegalAccessException ignored) {
}
}
public static void initializeValue(@NotNull ConfigurationProvider provider, @NotNull ConfigValue<?> value,
@NotNull String path, @NotNull String[] comments, boolean saveDefault) {
value.initialize(provider, path, comments);
if (saveDefault && value.getDefaultValue() != null && !provider.getConfiguration().contains(path)) {
value.setDefault();
}
}
public static String getSectionPath(@NotNull String name,
@Nullable String parentSection,
@Nullable ConfigPath pathAnnotation) {
String parent = parentSection != null ? parentSection + "." : "";
if (pathAnnotation != null && pathAnnotation.value().length() > 0) {
return parent + pathAnnotation.value();
} else {
return parent + getSectionName(name);
}
}
public static String getSectionName(String codeName) {
return codeName.toLowerCase().replace("_", "-");
}
}

View File

@ -0,0 +1,4 @@
package cc.carm.lib.configuration.core;
public abstract class ConfigurationRoot {
}

View File

@ -0,0 +1,17 @@
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 "";
}

View File

@ -0,0 +1,14 @@
package cc.carm.lib.configuration.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ConfigPath {
String value();
}

View File

@ -0,0 +1,42 @@
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 AbstractConfigBuilder<B extends AbstractConfigBuilder<B, T>, T> {
protected @Nullable ConfigurationProvider provider;
protected @Nullable String path;
protected @NotNull String[] comments = new String[0];
protected @Nullable T defaultValue;
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();
}
}

View File

@ -0,0 +1,42 @@
package cc.carm.lib.configuration.core.builder;
import cc.carm.lib.configuration.core.builder.list.ConfigListBuilder;
import cc.carm.lib.configuration.core.builder.map.ConfigMapBuilder;
import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.TreeMap;
public class ConfigBuilder {
public static <V> @NotNull ConfigValueBuilder<V> asValue(@NotNull Class<V> valueClass) {
return new ConfigValueBuilder<>(valueClass);
}
public static <V> @NotNull ConfigListBuilder<V> asList(@NotNull Class<V> valueClass) {
return new ConfigListBuilder<>(valueClass);
}
public static <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asMap(@NotNull Class<K> keyClass,
@NotNull Class<V> valueClass) {
return new ConfigMapBuilder<>(LinkedHashMap::new, keyClass, valueClass);
}
public static <K, V> @NotNull ConfigMapBuilder<HashMap<K, V>, K, V> asHashMap(@NotNull Class<K> keyClass,
@NotNull Class<V> valueClass) {
return asMap(keyClass, valueClass).supplier(HashMap::new);
}
public static <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> asLinkedMap(@NotNull Class<K> keyClass,
@NotNull Class<V> valueClass) {
return asMap(keyClass, valueClass);
}
public static <K extends Comparable<K>, V> @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap(@NotNull Class<K> keyClass,
@NotNull Class<V> valueClass) {
return asMap(keyClass, valueClass).supplier(TreeMap::new);
}
}

View File

@ -0,0 +1,46 @@
package cc.carm.lib.configuration.core.builder.list;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import org.jetbrains.annotations.NotNull;
public class ConfigListBuilder<V> {
protected final @NotNull Class<V> valueClass;
public ConfigListBuilder(@NotNull Class<V> valueClass) {
this.valueClass = valueClass;
}
public @NotNull <S> SourceListBuilder<S, V> from(@NotNull Class<S> sourceClass,
@NotNull ConfigDataFunction<Object, S> sourceParser,
@NotNull ConfigDataFunction<S, V> valueParser,
@NotNull ConfigDataFunction<V, S> valueSerializer,
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
return new SourceListBuilder<>(sourceClass, sourceParser, this.valueClass, valueParser, valueSerializer, sourceSerializer);
}
public @NotNull <S> SourceListBuilder<S, V> from(Class<S> sourceClass) {
return from(sourceClass,
ConfigDataFunction.required(), ConfigDataFunction.required(),
ConfigDataFunction.required(), ConfigDataFunction.required()
);
}
public @NotNull SourceListBuilder<Object, V> fromObject() {
return from(
Object.class, ConfigDataFunction.identity(),
ConfigDataFunction.castObject(valueClass),
ConfigDataFunction.identity(), ConfigDataFunction.toObject()
);
}
public @NotNull SourceListBuilder<String, V> fromString() {
return from(
String.class, ConfigDataFunction.castToString(),
ConfigDataFunction.castFromString(this.valueClass),
ConfigDataFunction.castToString(), ConfigDataFunction.toObject()
);
}
}

View File

@ -0,0 +1,70 @@
package cc.carm.lib.configuration.core.builder.list;
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.value.type.ConfiguredList;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class SourceListBuilder<S, V>
extends AbstractConfigBuilder<SourceListBuilder<S, V>, List<V>> {
protected final @NotNull Class<S> sourceClass;
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
protected final @NotNull Class<V> valueClass;
protected @NotNull ConfigDataFunction<S, V> valueParser;
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
public SourceListBuilder(@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
@NotNull ConfigDataFunction<V, S> valueSerializer,
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
this.sourceClass = sourceClass;
this.sourceParser = sourceParser;
this.sourceSerializer = sourceSerializer;
this.valueClass = valueClass;
this.valueParser = valueParser;
this.valueSerializer = valueSerializer;
}
public @NotNull SourceListBuilder<S, V> parseSource(ConfigDataFunction<Object, S> sourceParser) {
this.sourceParser = sourceParser;
return this;
}
public @NotNull SourceListBuilder<S, V> parseValue(ConfigDataFunction<S, V> valueParser) {
this.valueParser = valueParser;
return this;
}
public @NotNull SourceListBuilder<S, V> serializeValue(ConfigDataFunction<V, S> serializer) {
this.valueSerializer = serializer;
return this;
}
public @NotNull SourceListBuilder<S, V> serializeSource(ConfigDataFunction<S, Object> serializer) {
this.sourceSerializer = serializer;
return this;
}
@Override
protected @NotNull SourceListBuilder<S, V> getThis() {
return this;
}
@Override
public @NotNull ConfiguredList<V> build() {
return new ConfiguredList<>(
this.provider, this.path, this.comments,
this.valueClass, this.defaultValue,
this.sourceParser.andThen(this.valueParser),
this.valueSerializer.andThen(sourceSerializer)
);
}
}

View File

@ -0,0 +1,60 @@
package cc.carm.lib.configuration.core.builder.map;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.function.Supplier;
public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
protected final @NotNull Supplier<@NotNull M> supplier;
protected final @NotNull Class<K> keyClass;
protected final @NotNull Class<V> valueClass;
public ConfigMapBuilder(@NotNull Supplier<@NotNull M> supplier, @NotNull Class<K> keyClass, @NotNull Class<V> valueClass) {
this.supplier = supplier;
this.keyClass = keyClass;
this.valueClass = valueClass;
}
public <MAP extends Map<K, V>> ConfigMapBuilder<MAP, K, V> supplier(@NotNull Supplier<MAP> supplier) {
return new ConfigMapBuilder<>(supplier, keyClass, valueClass);
}
public <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> sourceClass,
@NotNull ConfigDataFunction<S, V> valueParser,
@NotNull ConfigDataFunction<V, S> valueSerializer) {
return new SourceMapBuilder<>(supplier,
keyClass, ConfigDataFunction.castFromString(this.keyClass), // #String -> key
sourceClass, ConfigDataFunction.castObject(sourceClass), // #Object -> source
valueClass, valueParser, // source -> value
ConfigDataFunction.castToString(), // key -> #String
valueSerializer/*value -> source*/,
ConfigDataFunction.toObject()/* source -> #Object */
);
}
public <S> SourceMapBuilder<M, S, K, V> from(@NotNull Class<S> sourceClass) {
return from(sourceClass, ConfigDataFunction.required(), ConfigDataFunction.required());
}
public SourceMapBuilder<M, String, K, V> fromString(@NotNull ConfigDataFunction<String, V> valueParser) {
return from(String.class, valueParser, ConfigDataFunction.castToString());
}
public SourceMapBuilder<M, String, K, V> fromString() {
return fromString(ConfigDataFunction.castFromString(this.valueClass));
}
public SourceMapBuilder<M, Object, K, V> fromObject(@NotNull ConfigDataFunction<Object, V> valueParser) {
return from(Object.class, valueParser, ConfigDataFunction.toObject());
}
public SourceMapBuilder<M, Object, K, V> fromObject() {
return fromObject(ConfigDataFunction.required());
}
}

View File

@ -0,0 +1,101 @@
package cc.carm.lib.configuration.core.builder.map;
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.value.type.ConfiguredMap;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.function.Supplier;
public class SourceMapBuilder<M extends Map<K, V>, S, K, V>
extends AbstractConfigBuilder<SourceMapBuilder<M, S, K, V>, M> {
protected final @NotNull Supplier<@NotNull M> supplier;
protected final @NotNull Class<K> keyClass;
protected @NotNull ConfigDataFunction<String, K> keyParser;
protected final @NotNull Class<S> sourceClass;
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
protected final @NotNull Class<V> valueClass;
protected @NotNull ConfigDataFunction<S, V> valueParser;
protected @NotNull ConfigDataFunction<K, String> keySerializer;
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
public SourceMapBuilder(@NotNull Supplier<@NotNull M> supplier,
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<S, V> valueParser,
@NotNull ConfigDataFunction<K, String> keySerializer,
@NotNull ConfigDataFunction<V, S> valueSerializer,
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
this.supplier = supplier;
this.keyClass = keyClass;
this.keyParser = keyParser;
this.valueClass = valueClass;
this.valueParser = valueParser;
this.sourceClass = sourceClass;
this.sourceParser = sourceParser;
this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
this.sourceSerializer = sourceSerializer;
}
public <MAP extends Map<K, V>> SourceMapBuilder<MAP, S, K, V> supplier(@NotNull Supplier<MAP> supplier) {
return new SourceMapBuilder<>(supplier,
keyClass, keyParser, sourceClass, sourceParser, valueClass, valueParser,
keySerializer, valueSerializer, sourceSerializer
);
}
public @NotNull SourceMapBuilder<M, S, K, V> parseKey(@NotNull ConfigDataFunction<String, K> parser) {
this.keyParser = parser;
return this;
}
public @NotNull SourceMapBuilder<M, S, K, V> parseSource(@NotNull ConfigDataFunction<Object, S> parser) {
this.sourceParser = parser;
return this;
}
public @NotNull SourceMapBuilder<M, S, K, V> parseValue(@NotNull ConfigDataFunction<S, V> parser) {
this.valueParser = parser;
return this;
}
public @NotNull SourceMapBuilder<M, S, K, V> serializeKey(@NotNull ConfigDataFunction<K, String> serializer) {
this.keySerializer = serializer;
return this;
}
public @NotNull SourceMapBuilder<M, S, K, V> serializeValue(@NotNull ConfigDataFunction<V, S> serializer) {
this.valueSerializer = serializer;
return this;
}
public @NotNull SourceMapBuilder<M, S, K, V> serializeSource(@NotNull ConfigDataFunction<S, Object> serializer) {
this.sourceSerializer = serializer;
return this;
}
@Override
protected @NotNull SourceMapBuilder<M, S, K, V> getThis() {
return this;
}
@Override
public @NotNull ConfiguredMap<K, V> build() {
return new ConfiguredMap<>(
this.provider, this.path, this.comments,
this.defaultValue, this.supplier,
this.keyClass, this.keyParser,
this.valueClass, this.sourceParser.andThen(this.valueParser),
this.keySerializer, this.valueSerializer.andThen(this.sourceSerializer)
);
}
}

View File

@ -0,0 +1,61 @@
package cc.carm.lib.configuration.core.builder.value;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
public class ConfigValueBuilder<V> {
protected final @NotNull Class<V> valueClass;
public ConfigValueBuilder(@NotNull Class<V> valueClass) {
this.valueClass = valueClass;
}
public @NotNull SectionValueBuilder<V> fromSection() {
return fromSection(ConfigValueParser.required(), ConfigDataFunction.required());
}
public @NotNull SectionValueBuilder<V> fromSection(@NotNull ConfigValueParser<ConfigurationWrapper, V> valueParser,
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> valueSerializer) {
return new SectionValueBuilder<>(this.valueClass, valueParser, valueSerializer);
}
public @NotNull <S> SourceValueBuilder<S, V> from(Class<S> sourceClass) {
return from(
sourceClass, ConfigDataFunction.required(), ConfigValueParser.required(),
ConfigDataFunction.required(), ConfigDataFunction.required()
);
}
public @NotNull <S> SourceValueBuilder<S, V> from(@NotNull Class<S> sourceClass,
@NotNull ConfigDataFunction<Object, S> sourceParser,
@NotNull ConfigValueParser<S, V> valueParser,
@NotNull ConfigDataFunction<V, S> valueSerializer,
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
return new SourceValueBuilder<>(
sourceClass, sourceParser, this.valueClass, valueParser,
valueSerializer, sourceSerializer
);
}
public @NotNull SourceValueBuilder<Object, V> fromObject() {
return from(
Object.class, ConfigDataFunction.identity(),
ConfigValueParser.castObject(valueClass),
ConfigDataFunction.identity(), ConfigDataFunction.toObject()
);
}
public @NotNull SourceValueBuilder<String, V> fromString() {
return from(
String.class, ConfigDataFunction.castToString(),
ConfigValueParser.parseString(this.valueClass),
ConfigDataFunction.castToString(), ConfigDataFunction.toObject()
);
}
}

View File

@ -0,0 +1,54 @@
package cc.carm.lib.configuration.core.builder.value;
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import cc.carm.lib.configuration.core.value.type.ConfiguredSection;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
public class SectionValueBuilder<V>
extends AbstractConfigBuilder<SectionValueBuilder<V>, V> {
protected final @NotNull Class<V> valueClass;
protected @NotNull ConfigValueParser<ConfigurationWrapper, V> parser;
protected @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
public SectionValueBuilder(@NotNull Class<V> valueClass,
@NotNull ConfigValueParser<ConfigurationWrapper, V> parser,
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
this.valueClass = valueClass;
this.parser = parser;
this.serializer = serializer;
}
@Override
protected @NotNull SectionValueBuilder<V> getThis() {
return this;
}
public @NotNull SectionValueBuilder<V> parseValue(ConfigValueParser<ConfigurationWrapper, V> valueParser) {
this.parser = valueParser;
return this;
}
public @NotNull SectionValueBuilder<V> serializeValue(ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
this.serializer = serializer;
return this;
}
@Override
public @NotNull ConfiguredSection<V> build() {
return new ConfiguredSection<>(
this.provider, this.path, this.comments,
this.valueClass, this.defaultValue,
this.parser, this.serializer
);
}
}

View File

@ -0,0 +1,67 @@
package cc.carm.lib.configuration.core.builder.value;
import cc.carm.lib.configuration.core.builder.AbstractConfigBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import cc.carm.lib.configuration.core.value.type.ConfiguredValue;
import org.jetbrains.annotations.NotNull;
public class SourceValueBuilder<S, V> extends AbstractConfigBuilder<SourceValueBuilder<S, V>, V> {
protected final @NotNull Class<S> sourceClass;
protected @NotNull ConfigDataFunction<Object, S> sourceParser;
protected final @NotNull Class<V> valueClass;
protected @NotNull ConfigValueParser<S, V> valueParser;
protected @NotNull ConfigDataFunction<S, Object> sourceSerializer;
protected @NotNull ConfigDataFunction<V, S> valueSerializer;
public SourceValueBuilder(@NotNull Class<S> sourceClass, @NotNull ConfigDataFunction<Object, S> sourceParser,
@NotNull Class<V> valueClass, @NotNull ConfigValueParser<S, V> valueParser,
@NotNull ConfigDataFunction<V, S> valueSerializer,
@NotNull ConfigDataFunction<S, Object> sourceSerializer) {
this.sourceClass = sourceClass;
this.sourceParser = sourceParser;
this.valueClass = valueClass;
this.valueParser = valueParser;
this.sourceSerializer = sourceSerializer;
this.valueSerializer = valueSerializer;
}
@Override
protected @NotNull SourceValueBuilder<S, V> getThis() {
return this;
}
public @NotNull SourceValueBuilder<S, V> parseSource(@NotNull ConfigDataFunction<Object, S> sourceParser) {
this.sourceParser = sourceParser;
return this;
}
public @NotNull SourceValueBuilder<S, V> parseValue(@NotNull ConfigValueParser<S, V> valueParser) {
this.valueParser = valueParser;
return this;
}
public @NotNull SourceValueBuilder<S, V> serializeValue(@NotNull ConfigDataFunction<V, S> serializer) {
this.valueSerializer = serializer;
return this;
}
public @NotNull SourceValueBuilder<S, V> serializeSource(@NotNull ConfigDataFunction<S, Object> serializer) {
this.sourceSerializer = serializer;
return this;
}
@Override
public @NotNull ConfiguredValue<V> build() {
return new ConfiguredValue<>(
this.provider, this.path, this.comments,
this.valueClass, this.defaultValue,
this.valueParser.compose(this.sourceParser),
this.valueSerializer.andThen(sourceSerializer)
);
}
}

View File

@ -0,0 +1,64 @@
package cc.carm.lib.configuration.core.function;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
@FunctionalInterface
public interface ConfigDataFunction<T, R> {
@NotNull R parse(@NotNull T data) throws Exception;
default <V> @NotNull ConfigDataFunction<T, V> andThen(@NotNull ConfigDataFunction<? super R, V> after) {
Objects.requireNonNull(after);
return ((data) -> after.parse(parse(data)));
}
@Contract(pure = true)
static <T> @NotNull ConfigDataFunction<T, ? super T> identity() {
return (input) -> input;
}
@Contract(pure = true)
static <T, V> @NotNull ConfigDataFunction<T, V> required() {
return (input) -> {
throw new IllegalArgumentException("Please specify the value parser.");
};
}
@Contract(pure = true)
static <T> @NotNull ConfigDataFunction<T, Object> toObject() {
return (input) -> input;
}
@Contract(pure = true)
static <V> @NotNull ConfigDataFunction<Object, V> castObject(Class<V> valueClass) {
return (input) -> {
if (valueClass.isInstance(input)) return valueClass.cast(input);
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
};
}
@Contract(pure = true)
static <V> @NotNull ConfigDataFunction<String, V> castFromString(Class<V> valueClass) {
return (input) -> {
if (valueClass.isInstance(input)) return valueClass.cast(input);
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
};
}
@Contract(pure = true)
static <T> @NotNull ConfigDataFunction<T, String> castToString() {
return (input) -> {
if (input instanceof String) return (String) input;
else return input.toString();
};
}
}

View File

@ -0,0 +1,164 @@
package cc.carm.lib.configuration.core.function;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@FunctionalInterface
public interface ConfigValueParser<T, R> {
@Nullable R parse(@NotNull T data, @Nullable R defaultValue) throws Exception;
default <V> ConfigValueParser<T, V> andThen(@NotNull ConfigValueParser<R, V> after) {
Objects.requireNonNull(after);
return ((data, defaultValue) -> {
R result = parse(data, null);
if (result == null) return defaultValue;
else return after.parse(result, defaultValue);
});
}
default <V> ConfigValueParser<V, R> compose(@NotNull ConfigDataFunction<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return ((data, defaultValue) -> {
T result = before.parse(data);
return parse(result, defaultValue);
});
}
@Contract(pure = true)
static <T> @NotNull ConfigValueParser<T, T> identity() {
return (input, defaultValue) -> input;
}
@Contract(pure = true)
static <T> @NotNull ConfigValueParser<T, Object> toObject() {
return (input, defaultValue) -> input;
}
@Contract(pure = true)
static <T, V> @NotNull ConfigValueParser<T, V> required() {
return (input, defaultValue) -> {
throw new IllegalArgumentException("Please specify the value parser.");
};
}
@Contract(pure = true)
static <V> @NotNull ConfigValueParser<Object, V> castObject(Class<V> valueClass) {
return (input, defaultValue) -> {
if (valueClass.isInstance(input)) return valueClass.cast(input);
else throw new IllegalArgumentException("Cannot cast value to " + valueClass.getName());
};
}
@Contract(pure = true)
static <V> @NotNull ConfigValueParser<String, V> parseString(Class<V> valueClass) {
return (input, defaultValue) -> {
if (valueClass.isInstance(input)) return valueClass.cast(input);
else throw new IllegalArgumentException("Cannot cast string to " + valueClass.getName());
};
}
@Contract(pure = true)
static @NotNull ConfigValueParser<Object, String> castToString() {
return (input, defaultValue) -> {
if (input instanceof String) return (String) input;
else return input.toString();
};
}
@Contract(pure = true)
static @NotNull <T> ConfigValueParser<T, String> castToString(Class<T> clazz) {
return (input, defaultValue) -> {
if (input instanceof String) return (String) input;
else return input.toString();
};
}
@Contract(pure = true)
static @NotNull ConfigValueParser<Object, Integer> intValue() {
return (input, defaultValue) -> {
if (input instanceof Integer) {
return (Integer) input;
} else if (input instanceof Number) {
return ((Number) input).intValue();
} else throw new IllegalArgumentException("Cannot cast value to " + Integer.class.getName());
};
}
@Contract(pure = true)
static @NotNull ConfigValueParser<Object, Short> shortValue() {
return (input, defaultValue) -> {
if (input instanceof Short) {
return (Short) input;
} else if (input instanceof Number) {
return ((Number) input).shortValue();
} else throw new IllegalArgumentException("Cannot cast value to " + Short.class.getName());
};
}
@Contract(pure = true)
static @NotNull ConfigValueParser<Object, Double> doubleValue() {
return (input, defaultValue) -> {
if (input instanceof Double) {
return (Double) input;
} else if (input instanceof Number) {
return ((Number) input).doubleValue();
} else throw new IllegalArgumentException("Cannot cast value to " + Double.class.getName());
};
}
@Contract(pure = true)
static @NotNull ConfigValueParser<Object, Byte> byteValue() {
return (input, defaultValue) -> {
if (input instanceof Byte) {
return (Byte) input;
} else if (input instanceof Number) {
return ((Number) input).byteValue();
} else throw new IllegalArgumentException("Cannot cast value to " + Byte.class.getName());
};
}
@Contract(pure = true)
static @NotNull ConfigValueParser<Object, Float> floatValue() {
return (input, defaultValue) -> {
if (input instanceof Float) {
return (Float) input;
} else if (input instanceof Number) {
return ((Number) input).floatValue();
} else throw new IllegalArgumentException("Cannot cast value to " + Float.class.getName());
};
}
@Contract(pure = true)
static @NotNull ConfigValueParser<Object, Long> longValue() {
return (input, defaultValue) -> {
if (input instanceof Long) {
return (Long) input;
} else if (input instanceof Number) {
return ((Number) input).longValue();
} else throw new IllegalArgumentException("Cannot cast value to " + Long.class.getName());
};
}
@Contract(pure = true)
static @NotNull ConfigValueParser<Object, Boolean> booleanValue() {
return (input, defaultValue) -> {
if (input instanceof Boolean) {
return (Boolean) input;
} else if (input instanceof String) {
String s = (String) input;
return Boolean.parseBoolean(s) || "yes".equalsIgnoreCase(s);
} else if (input instanceof Integer) {
return ((Integer) input) == 1;
} else throw new IllegalArgumentException("Cannot cast value to " + Boolean.class.getName());
};
}
}

View File

@ -0,0 +1,38 @@
package cc.carm.lib.configuration.core.source;
import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.core.ConfigurationRoot;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class ConfigurationProvider {
protected long updateTime;
public ConfigurationProvider() {
this.updateTime = System.currentTimeMillis();
}
public long getUpdateTime() {
return updateTime;
}
public boolean isExpired(long time) {
return this.updateTime > time;
}
public abstract @NotNull ConfigurationWrapper getConfiguration();
public abstract void reload() throws Exception;
public abstract void save() throws Exception;
public abstract void setComments(@NotNull String path, @NotNull String... comments);
public abstract @Nullable String[] getComments(@NotNull String path);
public void initialize(Class<? extends ConfigurationRoot> configClazz) {
ConfigInitializer.initialize(this, configClazz, true);
}
}

View File

@ -0,0 +1,144 @@
package cc.carm.lib.configuration.core.source;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
interface ConfigurationReader {
ConfigurationWrapper getWrapper();
default boolean isBoolean(@NotNull String path) {
return getWrapper().isType(path, Boolean.class);
}
default boolean getBoolean(@NotNull String path) {
return getBoolean(path, false);
}
@Contract("_, !null -> !null")
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
return getWrapper().get(path, def, ConfigValueParser.booleanValue());
}
default @Nullable Boolean isByte(@NotNull String path) {
return getWrapper().isType(path, Byte.class);
}
default @Nullable Byte getByte(@NotNull String path) {
return getByte(path, (byte) 0);
}
@Contract("_, !null -> !null")
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
return getWrapper().get(path, def, ConfigValueParser.byteValue());
}
default boolean isShort(@NotNull String path) {
return getWrapper().isType(path, Short.class);
}
default @Nullable Short getShort(@NotNull String path) {
return getShort(path, (short) 0);
}
@Contract("_, !null -> !null")
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
return getWrapper().get(path, def, ConfigValueParser.shortValue());
}
default boolean isInt(@NotNull String path) {
return getWrapper().isType(path, Integer.class);
}
default @Nullable Integer getInt(@NotNull String path) {
return getInt(path, 0);
}
@Contract("_, !null -> !null")
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
return getWrapper().get(path, def, ConfigValueParser.intValue());
}
default boolean isLong(@NotNull String path) {
return getWrapper().isType(path, Long.class);
}
default @Nullable Long getLong(@NotNull String path) {
return getLong(path, 0L);
}
@Contract("_, !null -> !null")
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
return getWrapper().get(path, def, ConfigValueParser.longValue());
}
default boolean isFloat(@NotNull String path) {
return getWrapper().isType(path, Float.class);
}
default @Nullable Float getFloat(@NotNull String path) {
return getFloat(path, 0.0F);
}
@Contract("_, !null -> !null")
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
return getWrapper().get(path, def, ConfigValueParser.floatValue());
}
default boolean isDouble(@NotNull String path) {
return getWrapper().isType(path, Double.class);
}
default @Nullable Double getDouble(@NotNull String path) {
return getDouble(path, 0.0D);
}
@Contract("_, !null -> !null")
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
return getWrapper().get(path, def, ConfigValueParser.doubleValue());
}
default boolean isChar(@NotNull String path) {
return getWrapper().isType(path, Boolean.class);
}
default @Nullable Character getChar(@NotNull String path) {
return getChar(path, null);
}
@Contract("_, !null -> !null")
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
return getWrapper().get(path, def, Character.class);
}
default boolean isString(@NotNull String path) {
return getWrapper().isType(path, String.class);
}
default @Nullable String getString(@NotNull String path) {
return getString(path, null);
}
@Contract("_, !null -> !null")
default @Nullable String getString(@NotNull String path, @Nullable String def) {
return getWrapper().get(path, def, String.class);
}
}

View File

@ -0,0 +1,72 @@
package cc.carm.lib.configuration.core.source;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Set;
public interface ConfigurationWrapper extends ConfigurationReader{
@Override
default ConfigurationWrapper getWrapper() {
return this;
}
@NotNull
Set<String> getKeys(boolean deep);
@NotNull
Map<String, Object> getValues(boolean deep);
void set(@NotNull String path, @Nullable Object value);
boolean contains(@NotNull String path);
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
return typeClass.isInstance(get(path));
}
@Nullable Object get(@NotNull String path);
default @Nullable <T> T get(@NotNull String path, @NotNull Class<T> clazz) {
return get(path, null, clazz);
}
default @Nullable <T> T get(@NotNull String path, @NotNull ConfigValueParser<Object, T> parser) {
return get(path, null, parser);
}
@Contract("_,!null,_->!null")
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class<T> clazz) {
return get(path, defaultValue, ConfigValueParser.castObject(clazz));
}
@Contract("_,!null,_->!null")
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
@NotNull ConfigValueParser<Object, T> parser) {
Object value = get(path);
if (value != null) {
try {
return parser.parse(value, defaultValue);
} catch (Exception e) {
e.printStackTrace();
}
}
return defaultValue;
}
boolean isList(@NotNull String path);
@Nullable List<?> getList(@NotNull String path);
boolean isConfigurationSection(@NotNull String path);
@Nullable
ConfigurationWrapper getConfigurationSection(@NotNull String path);
}

View File

@ -0,0 +1,83 @@
package cc.carm.lib.configuration.core.source.impl;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.Objects;
public abstract class FileConfigProvider extends ConfigurationProvider {
protected final @NotNull File file;
public FileConfigProvider(@NotNull File file) {
this.file = file;
}
public @NotNull File getFile() {
return file;
}
public void initializeFile(@Nullable String sourcePath) throws IOException {
if (getFile().exists()) return;
if (!getFile().getParentFile().exists() && !getFile().getParentFile().mkdirs()) {
throw new IOException("Failed to create directory " + file.getParentFile().getAbsolutePath());
}
if (!getFile().createNewFile()) {
throw new IOException("Failed to create file " + file.getAbsolutePath());
}
if (sourcePath == null) return;
try {
saveResource(sourcePath, true);
} catch (Exception ignored) {
}
}
public void saveResource(@NotNull String resourcePath, boolean replace)
throws NullPointerException, IOException, IllegalArgumentException {
Objects.requireNonNull(resourcePath, "ResourcePath cannot be null");
if (resourcePath.equals("")) throw new IllegalArgumentException("ResourcePath cannot be empty");
resourcePath = resourcePath.replace('\\', '/');
InputStream in = getResource(resourcePath);
if (in == null) throw new IllegalArgumentException("The resource '" + resourcePath + "' not exists");
int lastIndex = resourcePath.lastIndexOf('/');
File outDir = new File(file, resourcePath.substring(0, Math.max(lastIndex, 0)));
if (!outDir.exists() && !outDir.mkdirs()) throw new IOException("Failed to create directory " + outDir);
if (!file.exists() || replace) {
try {
OutputStream out = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.close();
in.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
@Nullable
public InputStream getResource(@NotNull String filename) {
try {
URL url = this.getClass().getClassLoader().getResource(filename);
if (url == null) return null;
URLConnection connection = url.openConnection();
connection.setUseCaches(false);
return connection.getInputStream();
} catch (IOException ex) {
return null;
}
}
}

View File

@ -0,0 +1,67 @@
package cc.carm.lib.configuration.core.util;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class MapFactory<S extends Map<K, V>, K, V> {
private final S map;
protected MapFactory(S map) {
this.map = map;
}
public MapFactory<S, K, V> put(K key, V value) {
this.map.put(key, value);
return this;
}
public MapFactory<S, K, V> remove(K key) {
this.map.remove(key);
return this;
}
public MapFactory<S, K, V> clear() {
this.map.clear();
return this;
}
public S build() {
return get();
}
public S get() {
return map;
}
public static <K, V> MapFactory<HashMap<K, V>, K, V> hashMap() {
return new MapFactory<>(new HashMap<>());
}
public static <K, V> MapFactory<HashMap<K, V>, K, V> hashMap(K firstKey, V firstValue) {
return MapFactory.<K, V>hashMap().put(firstKey, firstValue);
}
public static <K, V> MapFactory<LinkedHashMap<K, V>, K, V> linkedMap() {
return of(new LinkedHashMap<>());
}
public static <K, V> MapFactory<LinkedHashMap<K, V>, K, V> linkedMap(K firstKey, V firstValue) {
return MapFactory.<K, V>linkedMap().put(firstKey, firstValue);
}
public static <K extends Comparable<K>, V> MapFactory<TreeMap<K, V>, K, V> treeMap() {
return of(new TreeMap<>());
}
public static <K extends Comparable<K>, V> MapFactory<TreeMap<K, V>, K, V> treeMap(K firstKey, V firstValue) {
return MapFactory.<K, V>treeMap().put(firstKey, firstValue);
}
public static <M extends Map<K, V>, K, V> MapFactory<M, K, V> of(M map) {
return new MapFactory<>(map);
}
}

View File

@ -0,0 +1,40 @@
package cc.carm.lib.configuration.core.value;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class CachedConfigValue<T> extends ConfigValue<T> {
protected @Nullable T cachedValue;
protected long parsedTime = -1;
public CachedConfigValue(@Nullable ConfigurationProvider provider, @Nullable String sectionPath,
@NotNull String[] comments, @Nullable T defaultValue) {
super(provider, sectionPath, comments, defaultValue);
}
protected T updateCache(T value) {
this.parsedTime = System.currentTimeMillis();
this.cachedValue = value;
return getCachedValue();
}
public @Nullable T getCachedValue() {
return cachedValue;
}
public boolean isExpired() {
return this.parsedTime <= 0 || getProvider().isExpired(this.parsedTime);
}
protected final T useDefault() {
return useOrDefault(null);
}
protected final T useOrDefault(@Nullable T value) {
return updateCache(this.defaultValue == null ? value : this.defaultValue);
}
}

View File

@ -0,0 +1,110 @@
package cc.carm.lib.configuration.core.value;
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
import cc.carm.lib.configuration.core.builder.value.ConfigValueBuilder;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.Optional;
public abstract class ConfigValue<T> {
public static <V> ConfigValueBuilder<V> builder(Class<V> valueClass) {
return ConfigBuilder.asValue(valueClass);
}
public static <V> ConfigValue<V> of(Class<V> valueClass) {
return builder(valueClass).fromObject().build();
}
protected @Nullable T defaultValue;
protected @Nullable ConfigurationProvider provider;
protected @Nullable String configPath;
protected @NotNull String[] comments;
public ConfigValue(@Nullable ConfigurationProvider provider, @Nullable String configPath,
@NotNull String[] comments, @Nullable T defaultValue) {
this.provider = provider;
this.configPath = configPath;
this.comments = comments;
this.defaultValue = defaultValue;
}
public void initialize(@NotNull ConfigurationProvider provider, @NotNull String configPath,
@NotNull String... comments) {
if (this.provider == null) this.provider = provider;
if (this.configPath == null) this.configPath = configPath;
if (this.comments.length == 0) this.comments = comments;
this.provider.setComments(this.configPath, this.comments);
get();
}
public @Nullable T getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(@Nullable T defaultValue) {
this.defaultValue = defaultValue;
}
public abstract @Nullable T get();
public @Nullable T getOrDefault() {
return Optional.ofNullable(get()).orElse(getDefaultValue());
}
public @NotNull T getNotNull() {
return Objects.requireNonNull(getOrDefault(), "Value(" + configPath + ") is null.");
}
public @NotNull Optional<@Nullable T> getOptional() {
return Optional.ofNullable(get());
}
public abstract void set(@Nullable T value);
public void setDefault() {
Optional.ofNullable(getDefaultValue()).ifPresent(this::set);
}
public @NotNull ConfigurationProvider getProvider() {
return Optional.ofNullable(this.provider)
.orElseThrow(() -> new IllegalStateException("Value(" + configPath + ") does not have a provider."));
}
public final @NotNull ConfigurationWrapper getConfiguration() {
try {
return getProvider().getConfiguration();
} catch (Exception ex) {
throw new IllegalStateException("Value(" + configPath + ") has not been initialized", ex);
}
}
public @NotNull String getConfigPath() {
return Optional.ofNullable(this.configPath)
.orElseThrow(() -> new IllegalStateException("No section path provided."));
}
protected Object getValue() {
return getConfiguration().get(getConfigPath());
}
protected void setValue(@Nullable Object value) {
getConfiguration().set(getConfigPath(), value);
}
public String[] getComments() {
return comments;
}
public void setComments(String[] comments) {
this.comments = comments;
}
}

View File

@ -0,0 +1,72 @@
package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class ConfiguredList<V> extends CachedConfigValue<List<V>> {
protected final @NotNull Class<V> valueClass;
protected final @NotNull ConfigDataFunction<Object, V> parser;
protected final @NotNull ConfigDataFunction<V, Object> serializer;
public ConfiguredList(@Nullable ConfigurationProvider provider,
@Nullable String sectionPath, @NotNull String[] comments,
@NotNull Class<V> valueClass, @Nullable List<V> defaultValue,
@NotNull ConfigDataFunction<Object, V> parser,
@NotNull ConfigDataFunction<V, Object> serializer) {
super(provider, sectionPath, comments, defaultValue);
this.valueClass = valueClass;
this.parser = parser;
this.serializer = serializer;
}
@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 {
list.add(parser.parse(dataVal));
} catch (Exception e) {
e.printStackTrace();
}
}
return updateCache(list);
} else if (getCachedValue() != null) return getCachedValue();
else if (getDefaultValue() != null) return getDefaultValue();
else return new ArrayList<>();
}
@Override
public void set(@Nullable List<V> value) {
updateCache(value);
if (value == null) setValue(null);
else {
List<Object> data = new ArrayList<>();
for (V val : value) {
if (val == null) continue;
try {
data.add(serializer.parse(val));
} catch (Exception ex) {
ex.printStackTrace();
}
}
setValue(data);
}
}
}

View File

@ -0,0 +1,125 @@
package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.builder.ConfigBuilder;
import cc.carm.lib.configuration.core.builder.map.ConfigMapBuilder;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import cc.carm.lib.configuration.core.value.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> {
public static <K, V> @NotNull ConfigMapBuilder<LinkedHashMap<K, V>, K, V> builder(@NotNull Class<K> keyClass,
@NotNull Class<V> valueClass) {
return ConfigBuilder.asMap(keyClass, valueClass);
}
protected final @NotNull Supplier<? extends Map<K, V>> supplier;
protected final @NotNull Class<K> keyClass;
protected final @NotNull Class<V> valueClass;
protected final @NotNull ConfigDataFunction<String, K> keyParser;
protected final @NotNull ConfigDataFunction<Object, V> valueParser;
protected final @NotNull ConfigDataFunction<K, String> keySerializer;
protected final @NotNull ConfigDataFunction<V, Object> valueSerializer;
public ConfiguredMap(@Nullable ConfigurationProvider provider,
@Nullable String sectionPath, @NotNull String[] comments,
@Nullable Map<K, V> defaultValue, @NotNull Supplier<? extends Map<K, V>> supplier,
@NotNull Class<K> keyClass, @NotNull ConfigDataFunction<String, K> keyParser,
@NotNull Class<V> valueClass, @NotNull ConfigDataFunction<Object, V> valueParser,
@NotNull ConfigDataFunction<K, String> keySerializer,
@NotNull ConfigDataFunction<V, Object> valueSerializer) {
super(provider, sectionPath, comments, defaultValue);
this.supplier = supplier;
this.keyClass = keyClass;
this.valueClass = valueClass;
this.keyParser = keyParser;
this.valueParser = valueParser;
this.keySerializer = keySerializer;
this.valueSerializer = valueSerializer;
}
public @NotNull Class<K> getKeyClass() {
return keyClass;
}
public @NotNull Class<V> getValueClass() {
return valueClass;
}
public @NotNull ConfigDataFunction<String, K> getKeyParser() {
return keyParser;
}
public @NotNull ConfigDataFunction<Object, V> getValueParser() {
return valueParser;
}
public @NotNull ConfigDataFunction<K, String> getKeySerializer() {
return keySerializer;
}
public @NotNull ConfigDataFunction<V, Object> getValueSerializer() {
return valueSerializer;
}
@Override
public @NotNull Map<K, V> get() {
if (isExpired()) { // 已过时的数据需要重新解析一次
Map<K, V> map = supplier.get();
ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath());
if (section == null) return useOrDefault(map);
Set<String> keys = section.getKeys(false);
if (keys.isEmpty()) return useOrDefault(map);
for (String dataKey : keys) {
Object dataVal = section.get(dataKey);
if (dataVal == null) continue;
try {
K key = keyParser.parse(dataKey);
V value = valueParser.parse(dataVal);
map.put(key, value);
} catch (Exception e) {
e.printStackTrace();
}
}
return updateCache(map);
} else if (getCachedValue() != null) return getCachedValue();
else if (getDefaultValue() != null) return getDefaultValue();
else return supplier.get();
}
@Override
public void set(Map<K, V> value) {
updateCache(value);
if (value == null) setValue(null);
else {
Map<String, Object> data = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : value.entrySet()) {
try {
String key = keySerializer.parse(entry.getKey());
Object val = valueSerializer.parse(entry.getValue());
data.put(key, val);
} catch (Exception e) {
e.printStackTrace();
}
}
setValue(data);
}
}
}

View File

@ -0,0 +1,75 @@
package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import cc.carm.lib.configuration.core.value.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Optional;
public class ConfiguredSection<V> extends CachedConfigValue<V> {
protected final @NotNull Class<V> valueClass;
protected final @NotNull ConfigValueParser<ConfigurationWrapper, V> parser;
protected final @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer;
public ConfiguredSection(@Nullable ConfigurationProvider provider,
@Nullable String sectionPath, @NotNull String[] comments,
@NotNull Class<V> valueClass, @Nullable V defaultValue,
@NotNull ConfigValueParser<ConfigurationWrapper, V> parser,
@NotNull ConfigDataFunction<V, ? extends Map<String, Object>> serializer) {
super(provider, sectionPath, comments, defaultValue);
this.valueClass = valueClass;
this.parser = parser;
this.serializer = serializer;
}
public @NotNull Class<V> getValueClass() {
return valueClass;
}
public @NotNull ConfigValueParser<ConfigurationWrapper, V> getParser() {
return parser;
}
public @NotNull ConfigDataFunction<V, ? extends Map<String, Object>> getSerializer() {
return serializer;
}
@Override
public @Nullable V get() {
if (isExpired()) { // 已过时的数据需要重新解析一次
ConfigurationWrapper section = getConfiguration().getConfigurationSection(getConfigPath());
if (section == null) return useDefault();
try {
// 若未出现错误则直接更新缓存并返回
return updateCache(this.parser.parse(section, this.defaultValue));
} catch (Exception e) {
// 出现了解析错误提示并返回默认值
e.printStackTrace();
return useDefault();
}
} else return Optional.ofNullable(getCachedValue()).orElse(defaultValue);
}
@Override
public void set(V value) {
updateCache(value);
if (value == null) setValue(null);
else {
try {
setValue(serializer.parse(value));
// getConfiguration().createSection(getSectionPath(), serializer.parse(value));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,69 @@
package cc.carm.lib.configuration.core.value.type;
import cc.carm.lib.configuration.core.function.ConfigDataFunction;
import cc.carm.lib.configuration.core.function.ConfigValueParser;
import cc.carm.lib.configuration.core.source.ConfigurationProvider;
import cc.carm.lib.configuration.core.value.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
public class ConfiguredValue<V> extends CachedConfigValue<V> {
protected final @NotNull Class<V> valueClass;
protected final @NotNull ConfigValueParser<Object, V> parser;
protected final @NotNull ConfigDataFunction<V, Object> serializer;
public ConfiguredValue(@Nullable ConfigurationProvider provider,
@Nullable String sectionPath, @NotNull String[] comments,
@NotNull Class<V> valueClass, @Nullable V defaultValue,
@NotNull ConfigValueParser<Object, V> parser,
@NotNull ConfigDataFunction<V, Object> serializer) {
super(provider, sectionPath, comments, defaultValue);
this.valueClass = valueClass;
this.parser = parser;
this.serializer = serializer;
}
public @NotNull Class<V> getValueClass() {
return valueClass;
}
public @NotNull ConfigValueParser<Object, V> getParser() {
return parser;
}
@Override
public V get() {
if (isExpired()) { // 已过时的数据需要重新解析一次
Object value = getConfiguration().get(getConfigPath());
if (value == null) return useDefault(); // 获取的值不存在直接使用默认值
try {
// 若未出现错误则直接更新缓存并返回
return updateCache(this.parser.parse(value, this.defaultValue));
} catch (Exception e) {
// 出现了解析错误提示并返回默认值
e.printStackTrace();
return useDefault();
}
} else return Optional.ofNullable(getCachedValue()).orElse(defaultValue);
}
@Override
public void set(V value) {
updateCache(value);
if (value == null) setValue(null);
else {
try {
setValue(serializer.parse(value));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

47
impl/yaml/pom.xml Normal file
View File

@ -0,0 +1,47 @@
<?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>1.0.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>
</properties>
<artifactId>easyconfiguration-yaml</artifactId>
<packaging>jar</packaging>
<repositories>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/YamlConfiguration-Commented/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-core</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.bspfsystems</groupId>
<artifactId>yamlconfiguration-commented</artifactId>
<version>2.0.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,34 @@
package cc.carm.lib.configuration;
import cc.carm.lib.configuration.yaml.YamlConfigProvider;
import java.io.File;
import java.io.IOException;
public class EasyConfiguration {
public static YamlConfigProvider from(File file, String source) {
YamlConfigProvider provider = new YamlConfigProvider(file);
try {
provider.initializeFile(source);
provider.initialize();
} catch (IOException e) {
e.printStackTrace();
}
return provider;
}
public static YamlConfigProvider from(File file) {
return from(file, file.getName());
}
public static YamlConfigProvider from(String fileName) {
return from(fileName, fileName);
}
public static YamlConfigProvider from(String fileName, String source) {
return from(new File(fileName), source);
}
}

View File

@ -0,0 +1,35 @@
package cc.carm.lib.configuration.yaml;
import org.bspfsystems.yamlconfiguration.commented.CommentsProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
public class YamlComments implements CommentsProvider {
Map<String, String[]> comments = new HashMap<>();
protected Map<String, String[]> getComments() {
return comments;
}
public void set(@NotNull String path, @NotNull String... comments) {
if (comments.length == 0) {
getComments().remove(path);
} else {
getComments().put(path, comments);
}
}
public @Nullable String[] get(@NotNull String path) {
return getComments().get(path);
}
@Override
public String[] apply(String s) {
return get(s);
}
}

View File

@ -0,0 +1,49 @@
package cc.carm.lib.configuration.yaml;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
import org.bspfsystems.yamlconfiguration.commented.CommentedYamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
public class YamlConfigProvider extends FileConfigProvider {
YamlComments comments = new YamlComments();
CommentedYamlConfiguration configuration;
public YamlConfigProvider(@NotNull File file) {
super(file);
}
public void initialize() {
this.configuration = CommentedYamlConfiguration.loadConfiguration(comments, file);
}
@Override
public @NotNull ConfigurationWrapper getConfiguration() {
return YamlSectionWrapper.of(configuration);
}
@Override
public void reload() throws Exception {
configuration.load(getFile());
}
@Override
public void save() throws Exception {
configuration.save(getFile());
}
@Override
public void setComments(@NotNull String path, @NotNull String... comments) {
this.comments.set(path, comments);
}
@Override
public @Nullable String[] getComments(@NotNull String path) {
return this.comments.get(path);
}
}

View File

@ -0,0 +1,71 @@
package cc.carm.lib.configuration.yaml;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.bspfsystems.yamlconfiguration.configuration.ConfigurationSection;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class YamlSectionWrapper implements ConfigurationWrapper {
private final ConfigurationSection section;
private YamlSectionWrapper(ConfigurationSection section) {
this.section = section;
}
@Contract("!null->!null")
public static @Nullable YamlSectionWrapper of(@Nullable ConfigurationSection section) {
return section == null ? null : new YamlSectionWrapper(section);
}
@Override
public @NotNull Set<String> getKeys(boolean deep) {
return new LinkedHashSet<>(section.getKeys(deep));
}
@Override
public @NotNull Map<String, Object> getValues(boolean deep) {
return section.getValues(deep);
}
@Override
public void set(@NotNull String path, @Nullable Object value) {
this.section.set(path, value);
}
@Override
public boolean contains(@NotNull String path) {
return this.section.contains(path);
}
@Override
public @Nullable Object get(@NotNull String path) {
return this.section.get(path);
}
@Override
public boolean isList(@NotNull String path) {
return this.section.isList(path);
}
@Override
public @Nullable List<?> getList(@NotNull String path) {
return this.section.getList(path);
}
@Override
public boolean isConfigurationSection(@NotNull String path) {
return this.section.isConfigurationSection(path);
}
@Override
public @Nullable ConfigurationWrapper getConfigurationSection(@NotNull String path) {
return of(this.section.getConfigurationSection(path));
}
}

View File

@ -0,0 +1,53 @@
package config;
import cc.carm.lib.configuration.EasyConfiguration;
import cc.carm.lib.configuration.core.ConfigInitializer;
import cc.carm.lib.configuration.yaml.YamlConfigProvider;
import config.misc.TestUser;
import config.source.TestConfiguration;
import org.junit.Test;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class ConfigTester {
@Test
public void onTest() {
YamlConfigProvider provider = EasyConfiguration.from("target/config.yml", "config.yml");
ConfigInitializer.initialize(provider, TestConfiguration.class, true);
System.out.println("before: " + TestConfiguration.Sub.UUID_CONFIG_VALUE.get());
TestConfiguration.Sub.UUID_CONFIG_VALUE.set(UUID.randomUUID());
System.out.println("after: " + TestConfiguration.Sub.UUID_CONFIG_VALUE.get());
TestConfiguration.Sub.That.Operators.getNotNull().forEach(System.out::println);
List<UUID> operators = IntStream.range(0, 5).mapToObj(i -> UUID.randomUUID()).collect(Collectors.toList());
TestConfiguration.Sub.That.Operators.set(operators);
System.out.println(TestConfiguration.USER.get());
TestUser b = new TestUser(UUID.randomUUID().toString().substring(0, 3), UUID.randomUUID());
TestConfiguration.USER.set(b);
TestConfiguration.USERS.getNotNull().forEach((k, v) -> System.out.println(k + ": " + v));
LinkedHashMap<Integer, UUID> data = new LinkedHashMap<>();
for (int i = 0; i < 5; i++) {
data.put((int) (1000 * Math.random()), UUID.randomUUID());
}
TestConfiguration.USERS.set(data);
try {
provider.save();
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,38 @@
package config.misc;
import java.util.UUID;
public class TestUser {
public String name;
public UUID uuid;
public TestUser(String name, UUID uuid) {
this.name = name;
this.uuid = uuid;
}
public void setName(String name) {
this.name = name;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public UUID getUuid() {
return uuid;
}
@Override
public String toString() {
return "TestUser{" +
"name='" + name + '\'' +
", uuid=" + uuid +
'}';
}
}

View File

@ -0,0 +1,58 @@
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.builder.ConfigBuilder;
import cc.carm.lib.configuration.core.util.MapFactory;
import cc.carm.lib.configuration.core.value.ConfigValue;
import config.misc.TestUser;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
public class TestConfiguration extends ConfigurationRoot {
@ConfigComment({"User测试"})
public static final ConfigValue<TestUser> USER = ConfigBuilder
.asValue(TestUser.class).fromSection()
.defaults(new TestUser("Carm", UUID.randomUUID()))
.parseValue((section, defaultValue) -> new TestUser(
section.getString("name"),
UUID.fromString(section.getString("user.uuid", UUID.randomUUID().toString()))
)).serializeValue(user -> MapFactory.<String, Object>linkedMap()
.put("name", user.getName())
.put("user.uuid", user.getUuid().toString())
.build()
).build();
@ConfigComment({"[ID-UUID] 对照表", "", "用于测试Map类型的解析与序列化保存"})
public static final ConfigValue<Map<Integer, UUID>> USERS = ConfigBuilder
.asMap(Integer.class, UUID.class).fromString()
.parseKey(Integer::parseInt)
.parseValue(v -> Objects.requireNonNull(UUID.fromString(v)))
.build();
public static class Sub {
@ConfigPath("uuid")
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfigBuilder
.asValue(UUID.class).fromString()
.parseValue((data, defaultValue) -> UUID.fromString(data))
.build();
@ConfigPath("nothing")
public static class That {
public static final ConfigValue<List<UUID>> Operators = ConfigBuilder.asList(UUID.class).fromString()
.parseValue(s -> Objects.requireNonNull(UUID.fromString(s))).build();
}
}
}

View File

@ -0,0 +1 @@
something: 123123

47
platform/bungee/pom.xml Normal file
View File

@ -0,0 +1,47 @@
<?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>1.0.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>
</properties>
<artifactId>easyconfiguration-bungee</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-core</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.md-5</groupId>
<artifactId>bungeecord-api</artifactId>
<version>1.18-R0.1-SNAPSHOT</version>
<type>javadoc</type>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,34 @@
package cc.carm.lib.configuration;
import cc.carm.lib.configuration.bungee.BungeeConfigProvider;
import java.io.File;
import java.io.IOException;
public class EasyConfiguration {
public static BungeeConfigProvider from(File file, String source) {
BungeeConfigProvider provider = new BungeeConfigProvider(file);
try {
provider.initializeFile(source);
provider.initialize();
} catch (IOException e) {
e.printStackTrace();
}
return provider;
}
public static BungeeConfigProvider from(File file) {
return from(file, file.getName());
}
public static BungeeConfigProvider from(String fileName) {
return from(fileName, fileName);
}
public static BungeeConfigProvider from(String fileName, String source) {
return from(new File(fileName), source);
}
}

View File

@ -0,0 +1,54 @@
package cc.carm.lib.configuration.bungee;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
public class BungeeConfigProvider extends FileConfigProvider {
Configuration configuration;
public BungeeConfigProvider(@NotNull File file) {
super(file);
}
public void initialize() throws IOException {
this.configuration = getLoader().load(file);
}
@Override
public @NotNull ConfigurationWrapper getConfiguration() {
return BungeeSectionWrapper.of(configuration);
}
@Override
public void reload() throws Exception {
this.configuration = getLoader().load(file);
}
@Override
public void save() throws Exception {
getLoader().save(configuration, file);
}
@Override
public void setComments(@NotNull String path, @NotNull String... comments) {
}
@Override
public @Nullable String[] getComments(@NotNull String path) {
return null;
}
public static ConfigurationProvider getLoader() {
return ConfigurationProvider.getProvider(YamlConfiguration.class);
}
}

View File

@ -0,0 +1,70 @@
package cc.carm.lib.configuration.bungee;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import net.md_5.bungee.config.Configuration;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
public class BungeeSectionWrapper implements ConfigurationWrapper {
private final Configuration section;
private BungeeSectionWrapper(Configuration section) {
this.section = section;
}
@Contract("!null->!null")
public static @Nullable BungeeSectionWrapper of(@Nullable Configuration section) {
return section == null ? null : new BungeeSectionWrapper(section);
}
@Override
public @NotNull Set<String> getKeys(boolean deep) {
return new LinkedHashSet<>(section.getKeys());
}
@Override
public @NotNull Map<String, Object> getValues(boolean deep) {
return section.getKeys().stream()
.collect(Collectors.toMap(key -> key, section::get, (a, b) -> b, LinkedHashMap::new));
}
@Override
public void set(@NotNull String path, @Nullable Object value) {
this.section.set(path, value);
}
@Override
public boolean contains(@NotNull String path) {
return this.section.contains(path);
}
@Override
public @Nullable Object get(@NotNull String path) {
return this.section.get(path);
}
@Override
public boolean isList(@NotNull String path) {
return get(path) instanceof List<?>;
}
@Override
public @Nullable List<?> getList(@NotNull String path) {
return this.section.getList(path);
}
@Override
public boolean isConfigurationSection(@NotNull String path) {
return true; // No provided functions :( SRY
}
@Override
public @Nullable ConfigurationWrapper getConfigurationSection(@NotNull String path) {
return of(this.section.getSection(path));
}
}

39
platform/spigot/pom.xml Normal file
View File

@ -0,0 +1,39 @@
<?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>1.0.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>
</properties>
<artifactId>easyconfiguration-spigot</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-core</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.18.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,35 @@
package cc.carm.lib.configuration;
import cc.carm.lib.configuration.spigot.SpigotConfigProvider;
import java.io.File;
import java.io.IOException;
public class EasyConfiguration {
public static SpigotConfigProvider from(File file, String source) {
SpigotConfigProvider provider = new SpigotConfigProvider(file);
try {
provider.initializeFile(source);
provider.initialize();
} catch (IOException e) {
e.printStackTrace();
}
return provider;
}
public static SpigotConfigProvider from(File file) {
return from(file, file.getName());
}
public static SpigotConfigProvider from(String fileName) {
return from(fileName, fileName);
}
public static SpigotConfigProvider from(String fileName, String source) {
return from(new File(fileName), source);
}
}

View File

@ -0,0 +1,73 @@
package cc.carm.lib.configuration.commented;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.BaseConstructor;
import org.yaml.snakeyaml.error.YAMLException;
import org.yaml.snakeyaml.nodes.Node;
import org.yaml.snakeyaml.nodes.Tag;
import org.yaml.snakeyaml.representer.Representer;
import org.yaml.snakeyaml.serializer.Serializer;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* An hacky extension of {@link Yaml} that allows to write comments when dumping.
*/
public class CommentedYaml extends Yaml {
private final CommentsProvider commentsProvider;
public CommentedYaml(BaseConstructor constructor, Representer representer, DumperOptions dumperOptions, CommentsProvider commentsProvider) {
super(constructor, representer, dumperOptions);
this.commentsProvider = commentsProvider;
}
@Override
public String dump(Object data) {
List<Object> list = new ArrayList<>(1);
list.add(data);
return this.dumpAll(list.iterator());
}
@Override
public void dump(Object data, Writer output) {
List<Object> list = new ArrayList<>(1);
list.add(data);
this.dumpAll(list.iterator(), output, null);
}
@Override
public String dumpAll(Iterator<?> data) {
StringWriter buffer = new StringWriter();
this.dumpAll(data, buffer, null);
return buffer.toString();
}
@Override
public void dumpAll(Iterator<?> data, Writer output) {
this.dumpAll(data, output, null);
}
private void dumpAll(Iterator<?> data, Writer output, Tag rootTag) {
Serializer serializer = new Serializer(new CommentedEmitter(output, this.dumperOptions, this.commentsProvider), this.resolver, this.dumperOptions, rootTag);
try {
serializer.open();
while (data.hasNext()) {
Node node = this.representer.represent(data.next());
serializer.serialize(node);
}
serializer.close();
} catch (IOException var6) {
throw new YAMLException(var6);
}
}
}

View File

@ -0,0 +1,69 @@
package cc.carm.lib.configuration.commented;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.file.YamlConstructor;
import org.bukkit.configuration.file.YamlRepresenter;
import org.jetbrains.annotations.NotNull;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.representer.Representer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
/**
* A yaml file with comments on certain properties, as returned by the given {@link CommentsProvider}.
* Unlike {@link YamlConfiguration}, this class does not provide a header support.
*/
public class CommentedYamlConfiguration extends YamlConfiguration {
private final DumperOptions yamlOptions = new DumperOptions();
private final Representer yamlRepresenter = new YamlRepresenter();
private final CommentedYaml yaml;
public CommentedYamlConfiguration(CommentsProvider commentsProvider) {
this.yaml = new CommentedYaml(new YamlConstructor(), this.yamlRepresenter, this.yamlOptions, commentsProvider);
}
public static CommentedYamlConfiguration loadConfiguration(CommentsProvider commentsProvider, File file) {
CommentedYamlConfiguration config = new CommentedYamlConfiguration(commentsProvider);
try {
config.load(file);
} catch (FileNotFoundException ignored) {
} catch (IOException | InvalidConfigurationException var4) {
var4.printStackTrace();
}
return config;
}
public static CommentedYamlConfiguration loadConfiguration(CommentsProvider commentsProvider, Reader reader) {
CommentedYamlConfiguration config = new CommentedYamlConfiguration(commentsProvider);
try {
config.load(reader);
} catch (IOException | InvalidConfigurationException ex) {
ex.printStackTrace();
}
return config;
}
@Override
public @NotNull String saveToString() {
this.yamlOptions.setIndent(this.options().indent());
this.yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
this.yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
String dump = this.yaml.dump(this.getValues(false));
if (dump.equals("{}\n")) {
dump = "";
}
// No header support.
return dump;
}
}

View File

@ -0,0 +1,7 @@
package cc.carm.lib.configuration.commented;
import java.util.function.Function;
public interface CommentsProvider extends Function<String, String[]> {
}

View File

@ -0,0 +1,35 @@
package cc.carm.lib.configuration.spigot;
import cc.carm.lib.configuration.commented.CommentsProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
public class ConfigComments implements CommentsProvider {
Map<String, String[]> comments = new HashMap<>();
protected Map<String, String[]> getComments() {
return comments;
}
public void set(@NotNull String path, @NotNull String... comments) {
if (comments.length == 0) {
getComments().remove(path);
} else {
getComments().put(path, comments);
}
}
public @Nullable String[] get(@NotNull String path) {
return getComments().get(path);
}
@Override
public String[] apply(String s) {
return get(s);
}
}

View File

@ -0,0 +1,49 @@
package cc.carm.lib.configuration.spigot;
import cc.carm.lib.configuration.commented.CommentedYamlConfiguration;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import cc.carm.lib.configuration.core.source.impl.FileConfigProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
public class SpigotConfigProvider extends FileConfigProvider {
ConfigComments comments = new ConfigComments();
CommentedYamlConfiguration configuration;
public SpigotConfigProvider(@NotNull File file) {
super(file);
}
public void initialize() {
this.configuration = CommentedYamlConfiguration.loadConfiguration(comments, file);
}
@Override
public @NotNull ConfigurationWrapper getConfiguration() {
return SpigotSectionWrapper.of(configuration);
}
@Override
public void reload() throws Exception {
configuration.load(getFile());
}
@Override
public void save() throws Exception {
configuration.save(getFile());
}
@Override
public void setComments(@NotNull String path, @NotNull String... comments) {
this.comments.set(path, comments);
}
@Override
public @Nullable String[] getComments(@NotNull String path) {
return this.comments.get(path);
}
}

View File

@ -0,0 +1,71 @@
package cc.carm.lib.configuration.spigot;
import cc.carm.lib.configuration.core.source.ConfigurationWrapper;
import org.bukkit.configuration.ConfigurationSection;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class SpigotSectionWrapper implements ConfigurationWrapper {
private final ConfigurationSection section;
private SpigotSectionWrapper(ConfigurationSection section) {
this.section = section;
}
@Contract("!null->!null")
public static @Nullable SpigotSectionWrapper of(@Nullable ConfigurationSection section) {
return section == null ? null : new SpigotSectionWrapper(section);
}
@Override
public @NotNull Set<String> getKeys(boolean deep) {
return new LinkedHashSet<>(section.getKeys(deep));
}
@Override
public @NotNull Map<String, Object> getValues(boolean deep) {
return section.getValues(deep);
}
@Override
public void set(@NotNull String path, @Nullable Object value) {
this.section.set(path, value);
}
@Override
public boolean contains(@NotNull String path) {
return this.section.contains(path);
}
@Override
public @Nullable Object get(@NotNull String path) {
return this.section.get(path);
}
@Override
public boolean isList(@NotNull String path) {
return this.section.isList(path);
}
@Override
public @Nullable List<?> getList(@NotNull String path) {
return this.section.getList(path);
}
@Override
public boolean isConfigurationSection(@NotNull String path) {
return this.section.isConfigurationSection(path);
}
@Override
public @Nullable ConfigurationWrapper getConfigurationSection(@NotNull String path) {
return of(this.section.getConfigurationSection(path));
}
}

302
pom.xml Normal file
View File

@ -0,0 +1,302 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<java.version>8</java.version>
<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>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId>
<packaging>pom</packaging>
<version>1.0.0</version>
<modules>
<module>core</module>
<module>impl/yaml</module>
<module>platform/spigot</module>
<module>platform/bungee</module>
</modules>
<name>EasyConfiguration</name>
<description>轻松(做)配置,简单便捷的通用配置文件加载、读取与更新工具,可自定义配置格式。</description>
<url>https://github.com/CarmJos/EasyConfiguration</url>
<developers>
<developer>
<id>CarmJos</id>
<name>Carm Jos</name>
<email>carm@carm.cc</email>
<url>https://www.carm.cc</url>
<timezone>Asia/Shanghai</timezone>
</developer>
</developers>
<scm>
<connection>scm:git:git@github.com:CarmJos/Easy.EasyConfiguration</connection>
<developerConnection>scm:git:git@github.com:CarmJos/EasyConfiguration.git</developerConnection>
<url>https://github.com/CarmJos/EasyConfiguration</url>
<tag>HEAD</tag>
</scm>
<licenses>
<license>
<name>The MIT License</name>
<url>https://opensource.org/licenses/MIT</url>
</license>
</licenses>
<issueManagement>
<system>GitHub Issues</system>
<url>https://github.com/CarmJos/EasyConfiguration/issues</url>
</issueManagement>
<ciManagement>
<system>GitHub Actions</system>
<url>https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml</url>
</ciManagement>
<repositories>
<repository>
<id>carm-repo</id>
<name>Carm's Repo</name>
<url>https://repo.carm.cc/repository/maven-public/</url>
</repository>
<repository>
<id>nexus</id>
<url>https://mvn.lumine.io/repository/maven-public/</url>
</repository>
<repository>
<id>central</id>
<url>https://repo1.maven.org/maven2/</url>
</repository>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
</repository>
</repositories>
<distributionManagement>
<downloadUrl>https://github.com/CarmJos/EasyConfiguration/releases</downloadUrl>
<site>
<id>javadoc</id>
<name>EasySQL JavaDoc (on Github Pages)</name>
<url>https://CarmJos.github.io/EasyConfiguration</url>
</site>
</distributionManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>23.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<autoVersionSubmodules>true</autoVersionSubmodules>
<useReleaseProfile>false</useReleaseProfile>
<releaseProfiles>release</releaseProfiles>
<goals>deploy</goals>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.3.2</version>
<configuration>
<classifier>javadoc</classifier>
<detectJavaApiLink>false</detectJavaApiLink>
<encoding>UTF-8</encoding>
<charset>UTF-8</charset>
<docencoding>UTF-8</docencoding>
<locale>zh_CN</locale>
<includeDependencySources>true</includeDependencySources>
<dependencySourceIncludes>
<dependencySourceInclude>cc.carm.lib:*</dependencySourceInclude>
</dependencySourceIncludes>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<compilerArgument>-parameters</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/MANIFEST.MF</exclude>
<exclude>META-INF/*.txt</exclude>
</excludes>
</filter>
</filters>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<profiles>
<profile>
<id>ossrh</id>
<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>
</profile>
<profile>
<id>github</id>
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
</repository>
</distributionManagement>
</profile>
</profiles>
</project>