1
mirror of https://github.com/CarmJos/EasyConfiguration.git synced 2026-06-04 18:48:20 +08:00

Compare commits

...

193 Commits

Author SHA1 Message Date
renovate[bot] 85266283c4 fix(deps): update dependency com.typesafe:config to v1.4.9 (#243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-03 23:54:59 +00:00
renovate[bot] b0a9f6505c fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.8.0 (#241)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-29 02:01:07 +00:00
renovate[bot] 8c40eb20ea chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.6 (#240)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-28 00:33:52 +00:00
renovate[bot] 25ff7ab350 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.7.1 (#238)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-27 17:17:07 +00:00
renovate[bot] ad90251596 chore(deps): update log4j2 monorepo to v2.26.0 (#236)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-07 14:42:19 +00:00
renovate[bot] ed05374054 fix(deps): update dependency com.typesafe:config to v1.4.8 (#234)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-05 14:36:51 +00:00
renovate[bot] b5681f2412 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.7.0 (#233)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-01 01:59:52 +00:00
renovate[bot] bd9923ab7e fix(deps): update dependency com.typesafe:config to v1.4.7 (#231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-28 15:14:18 +00:00
renovate[bot] 9dece6004e fix(deps): update dependency com.google.code.gson:gson to v2.14.0 (#230)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-24 00:34:46 +00:00
renovate[bot] 654782afe1 fix(deps): update kotlin monorepo to v2.3.21 (#223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-23 14:42:12 +00:00
renovate[bot] 903f0239b2 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.6.5 (#228)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-04-09 13:37:56 +00:00
renovate[bot] dda929c0a9 chore(deps): update log4j2 monorepo to v2.25.4 (#227)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-28 16:53:33 +00:00
renovate[bot] 2482048449 chore(deps): update dependency org.jetbrains.dokka:dokka-maven-plugin to v2.2.0 (#225)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-26 14:33:40 +00:00
carm f54ee83938 feat(holder): Add file type multi configuration. #169 2026-03-11 11:24:27 +08:00
carm f5316eb320 feat(holder): Add file type multi configuration. #169 2026-03-11 11:16:19 +08:00
carm 500eebefde feat(holder): Try to support MultiConfiguration. #169 2026-03-11 11:02:11 +08:00
renovate[bot] d9cb95b2e9 chore(deps): update actions/upload-artifact action to v7 2026-03-10 08:05:30 +08:00
renovate[bot] ce6554c3f1 chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.2 (#221)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-05 14:36:14 +00:00
renovate[bot] 7dcd690a05 fix(deps): update dependency org.yaml:snakeyaml to v2.6 (#219)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-27 01:25:07 +00:00
renovate[bot] f376f22ad0 fix(deps): update dependency com.typesafe:config to v1.4.6 (#218)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-24 19:45:53 +00:00
renovate[bot] ab586c4b00 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.6.4 (#216)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-23 12:44:42 +00:00
renovate[bot] 64cbfaa974 chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.5 (#215)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-21 17:50:54 +00:00
dependabot[bot] 3cab343919 build(deps): bump kotlin.version from 2.3.0 to 2.3.10
Bumps `kotlin.version` from 2.3.0 to 2.3.10.

Updates `org.jetbrains.kotlin:kotlin-stdlib-jdk8` from 2.3.0 to 2.3.10
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.3.0...v2.3.10)

Updates `org.jetbrains.kotlin:kotlin-maven-plugin` from 2.3.0 to 2.3.10

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib-jdk8
  dependency-version: 2.3.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-maven-plugin
  dependency-version: 2.3.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-21 00:56:59 +08:00
renovate[bot] 1ad8c1f407 chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.15.0 (#207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-19 00:58:38 +00:00
renovate[bot] 4e6db50049 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.6.3 (#209)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-18 22:45:39 +00:00
renovate[bot] e62cef5644 fix(deps): update dependency org.jetbrains:annotations to v26.1.0 (#214)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-18 18:56:13 +00:00
dependabot[bot] d6a56003aa build(deps): bump org.sonatype.central:central-publishing-maven-plugin
Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.9.0 to 0.10.0.
- [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits)

---
updated-dependencies:
- dependency-name: org.sonatype.central:central-publishing-maven-plugin
  dependency-version: 0.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-31 02:46:18 +08:00
renovate[bot] 8a5870300d chore(deps): update actions/upload-artifact action to v6 2025-12-29 08:50:26 +08:00
dependabot[bot] 42885b2a12 build(deps): bump kotlin.version from 2.2.21 to 2.3.0
Bumps `kotlin.version` from 2.2.21 to 2.3.0.

Updates `org.jetbrains.kotlin:kotlin-stdlib-jdk8` from 2.2.21 to 2.3.0
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/v2.3.0/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.2.21...v2.3.0)

Updates `org.jetbrains.kotlin:kotlin-maven-plugin` from 2.2.21 to 2.3.0

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib-jdk8
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: org.jetbrains.kotlin:kotlin-maven-plugin
  dependency-version: 2.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-29 08:50:04 +08:00
renovate[bot] 96216d5126 chore(deps): update log4j2 monorepo to v2.25.3 (#204)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-16 21:58:48 +00:00
renovate[bot] d890ce1b94 chore(deps): update dependency org.apache.maven.plugins:maven-release-plugin to v3.3.1 (#201)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-13 14:29:02 +00:00
renovate[bot] d1ff4f7014 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.6.2 (#199)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-08 21:33:41 +00:00
renovate[bot] 9f3089566c chore(deps): update dependency org.apache.maven.plugins:maven-release-plugin to v3.3.0 (#198)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-12-03 20:54:54 +00:00
renovate[bot] 79105bf400 chore(deps): update actions/checkout action to v6 2025-12-01 09:31:45 +08:00
renovate[bot] 2b0b74af92 chore(deps): update dependency org.apache.maven.plugins:maven-source-plugin to v3.4.0 (#197)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-29 05:37:12 +00:00
renovate[bot] c65d164167 chore(deps): update dependency org.apache.maven.plugins:maven-jar-plugin to v3.5.0 (#195)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-15 17:30:30 +00:00
renovate[bot] 74bde0c7ea chore(deps): update dependency org.apache.maven.plugins:maven-release-plugin to v3.2.0 (#194)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-09 00:57:07 +00:00
carm 7fba61cb64 docs: Use english version issues templates. 2025-11-06 03:07:02 +08:00
renovate[bot] b609e1d174 chore(deps): update actions/upload-artifact action to v5 2025-10-30 10:54:38 +08:00
renovate[bot] bdbd484690 fix(deps): update kotlin monorepo to v2.2.21 (#191)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-23 14:08:33 +00:00
renovate[bot] cc5383b0f2 chore(deps): update github/codeql-action action to v4 2025-10-23 10:37:05 +08:00
renovate[bot] 35d2653f56 chore(deps): update dependency org.jetbrains.dokka:dokka-maven-plugin to v2.1.0 (#190)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-15 18:54:14 +00:00
carm d6fa7710dc fix: Try to ignore errors when applying replacer 2025-10-09 02:58:21 +08:00
renovate[bot] fdb6d81bf0 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.6.1 (#186)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 14:26:30 +00:00
huanmeng-qwq 6a03d446a3 fix: https://github.com/CarmJos/configured/pull/185#pullrequestreview-3273500562 2025-09-27 15:56:09 +08:00
huanmeng-qwq 4a0e6b0676 refactor deserialize, add set collection dsl 2025-09-27 15:56:09 +08:00
carm fc4a11bc3e docs: Add more wiki files. 2025-09-27 02:29:58 +08:00
carm e95abd6b37 docs: Add more wiki files. 2025-09-27 02:27:29 +08:00
carm 0b4304474e docs: Add more wiki files. 2025-09-27 02:20:07 +08:00
carm f36fb81249 feat: Add "getOr" function for values. 2025-09-27 02:19:48 +08:00
carm b17c157e18 refactor(collection): Refactor builders of collection types (including List and Set). 2025-09-27 02:09:42 +08:00
carm c86985017f refactor(builder): Refactor builder functions to support custom adapters. 2025-09-27 00:55:38 +08:00
renovate[bot] cb7562cd8c chore(deps): update dependency org.sonatype.central:central-publishing-maven-plugin to v0.9.0 2025-09-24 09:19:25 +08:00
renovate[bot] ae6aaa0461 chore(deps): update log4j2 monorepo to v2.25.2 (#183)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 12:25:53 +00:00
renovate[bot] c96a6c7e49 chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.14.1 (#182)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 09:14:52 +00:00
renovate[bot] 74b5e80cbc chore(deps): update dependency org.apache.maven.plugins:maven-javadoc-plugin to v3.12.0 (#181)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-21 08:55:00 +00:00
renovate[bot] 51c5c333e9 chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.4 (#180)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-14 02:59:18 +00:00
renovate[bot] f09ed5bbc5 chore(deps): update dependency org.apache.maven.plugins:maven-shade-plugin to v3.6.1 (#179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-13 23:59:11 +00:00
dependabot[bot] f0abdd87e3 build(deps): bump com.typesafe:config from 1.4.4 to 1.4.5
Bumps [com.typesafe:config](https://github.com/lightbend/config) from 1.4.4 to 1.4.5.
- [Release notes](https://github.com/lightbend/config/releases)
- [Changelog](https://github.com/lightbend/config/blob/main/NEWS.md)
- [Commits](https://github.com/lightbend/config/compare/v1.4.4...v1.4.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-12 08:52:17 +08:00
renovate[bot] 7f091669c7 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.6.0 2025-09-12 08:52:10 +08:00
carm 8dbee35757 ci: Update renovate.json 2025-09-12 08:19:16 +08:00
dependabot[bot] 1a2eaff1e6 build(deps): bump kotlin.version from 2.2.10 to 2.2.20
Bumps `kotlin.version` from 2.2.10 to 2.2.20.

Updates `org.jetbrains.kotlin:kotlin-stdlib-jdk8` from 2.2.10 to 2.2.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v2.2.10...v2.2.20)

Updates `org.jetbrains.kotlin:kotlin-maven-plugin` from 2.2.10 to 2.2.20

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib-jdk8
  dependency-version: 2.2.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: org.jetbrains.kotlin:kotlin-maven-plugin
  dependency-version: 2.2.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-11 10:52:07 +08:00
renovate[bot] 36364bc09b fix(deps): update dependency com.google.code.gson:gson to v2.13.2 2025-09-11 10:51:58 +08:00
carm fcb4ced610 feat: Make function interface "serializable" 2025-09-10 08:22:37 +08:00
carm 5df66e59d2 docs: Add Community Standards pages 2025-09-02 18:03:42 +08:00
carm 809cd5b268 docs: Add Community Standards pages 2025-09-02 18:00:38 +08:00
carm 661527a80f docs: Add more pages 2025-09-02 17:40:59 +08:00
carm 9883e75f05 docs: Add more pages 2025-09-02 17:09:40 +08:00
carm c3297d168f docs: Add donation link. 2025-09-02 16:56:46 +08:00
renovate[bot] 627e0ba391 fix(deps): update dependency org.yaml:snakeyaml to v2.5 2025-09-02 16:44:23 +08:00
renovate[bot] b29935ae47 fix(deps): update dependency org.jetbrains:annotations to v26.0.2-1 2025-09-02 16:44:15 +08:00
carm e1a907203e docs: Add wiki module. 2025-09-02 16:43:20 +08:00
carm 3cd1ddb3ad docs: Add wiki module. 2025-09-02 16:42:36 +08:00
renovate[bot] b46e116712 chore(deps): update actions/setup-java action to v5 2025-08-21 23:41:56 +08:00
dependabot[bot] 44194d6055 build(deps): bump org.apache.maven.plugins:maven-javadoc-plugin
Bumps [org.apache.maven.plugins:maven-javadoc-plugin](https://github.com/apache/maven-javadoc-plugin) from 3.11.2 to 3.11.3.
- [Release notes](https://github.com/apache/maven-javadoc-plugin/releases)
- [Commits](https://github.com/apache/maven-javadoc-plugin/compare/maven-javadoc-plugin-3.11.2...maven-javadoc-plugin-3.11.3)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-21 23:15:49 +08:00
renovate[bot] 47fd058f44 chore(deps): update actions/checkout action to v5 2025-08-15 19:29:18 +08:00
renovate[bot] c73fcafb15 fix(deps): update kotlin monorepo to v2.2.10 2025-08-15 19:28:11 +08:00
carm beb6d93f7b build: Fixed wrong scm connection. 2025-07-28 15:50:46 +08:00
dependabot[bot] d62aa14ca7 build(deps): bump org.apache.maven.plugins:maven-gpg-plugin
Bumps [org.apache.maven.plugins:maven-gpg-plugin](https://github.com/apache/maven-gpg-plugin) from 3.2.7 to 3.2.8.
- [Release notes](https://github.com/apache/maven-gpg-plugin/releases)
- [Commits](https://github.com/apache/maven-gpg-plugin/compare/maven-gpg-plugin-3.2.7...maven-gpg-plugin-3.2.8)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 03:32:51 +08:00
dependabot[bot] a439ca5cde build(deps): bump com.typesafe:config from 1.4.3 to 1.4.4
Bumps [com.typesafe:config](https://github.com/lightbend/config) from 1.4.3 to 1.4.4.
- [Release notes](https://github.com/lightbend/config/releases)
- [Changelog](https://github.com/lightbend/config/blob/main/NEWS.md)
- [Commits](https://github.com/lightbend/config/compare/v1.4.3...v1.4.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-21 03:32:45 +08:00
renovate[bot] 0736153fd6 chore(deps): update log4j2 monorepo to v2.25.1 2025-07-21 03:32:27 +08:00
carm 0bce5f1a22 build: Add project module names 2025-07-21 02:31:27 +08:00
carm feb43bb382 fix(list): Fixed error when getting the section list 2025-07-09 15:41:31 +08:00
carm 0bfd15aaad feat(record): Support record type values adapter. 2025-07-02 11:04:27 +08:00
carm 15f395a8e0 test(record): Add testcase for nullable values. 2025-06-26 03:04:21 +08:00
carm a61040a0e2 ci(jdk): Using JDK 21. 2025-06-26 02:28:32 +08:00
carm 8766b4d77b feat(adapter): Support complex ParameterizedType's serialize & deserialize. 2025-06-26 02:24:42 +08:00
carm 8c1214612a feat(adapter): Support complex ParameterizedType's serialize & deserialize. 2025-06-25 09:31:07 +08:00
carm 608d92f834 feat(record): Support record type values adapter. 2025-06-25 07:10:26 +08:00
carm ad6ab9eb36 feat(temp): Add a "temporary source" for Testing/FastDev cases. (Use default config in codes first.) 2025-06-25 07:10:04 +08:00
dependabot[bot] 0eda8d8a0f build(deps-dev): bump log4j.version from 2.24.3 to 2.25.0
Bumps `log4j.version` from 2.24.3 to 2.25.0.

Updates `org.apache.logging.log4j:log4j-api` from 2.24.3 to 2.25.0

Updates `org.apache.logging.log4j:log4j-core` from 2.24.3 to 2.25.0

Updates `org.apache.logging.log4j:log4j-slf4j-impl` from 2.24.3 to 2.25.0

---
updated-dependencies:
- dependency-name: org.apache.logging.log4j:log4j-api
  dependency-version: 2.25.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
- dependency-name: org.apache.logging.log4j:log4j-core
  dependency-version: 2.25.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
- dependency-name: org.apache.logging.log4j:log4j-slf4j-impl
  dependency-version: 2.25.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-25 06:29:38 +08:00
dependabot[bot] b19691b5a4 build(deps): bump org.sonatype.central:central-publishing-maven-plugin
Bumps [org.sonatype.central:central-publishing-maven-plugin](https://github.com/sonatype/central-publishing-maven-plugin) from 0.7.0 to 0.8.0.
- [Commits](https://github.com/sonatype/central-publishing-maven-plugin/commits)

---
updated-dependencies:
- dependency-name: org.sonatype.central:central-publishing-maven-plugin
  dependency-version: 0.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-25 06:29:28 +08:00
renovate[bot] c7331aa556 fix(deps): update kotlin monorepo to v2.2.0 2025-06-25 06:29:18 +08:00
carm a440c050e5 feat(type): Add ofMap(K,V) method for ValueType 2025-06-23 15:27:52 +08:00
renovate[bot] b9d45a9bb9 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.5.1 2025-06-10 22:45:19 +08:00
carm 67482de7a9 docs: drop visitors counter 2025-05-14 04:25:23 +08:00
carm 04bedbef07 style: Reformatted code with .editorconfig 2025-05-14 04:24:56 +08:00
carm a4abfb733a style: Reformatted code with .editorconfig 2025-05-14 04:22:48 +08:00
carm 76d276436b ci(deploy): Skip deployment for demo project. 2025-05-14 03:55:54 +08:00
carm e7198f22d5 ci(deploy): Skip deployment for demo project. 2025-05-14 03:48:54 +08:00
carm 5bd20e173f ci(deploy): Skip deployment for demo project. 2025-05-14 03:41:15 +08:00
carm 15c4bb13e8 build(repo): Use official repositories. 2025-05-14 03:33:45 +08:00
dependabot[bot] b74eb5c035 build(deps): bump com.google.code.gson:gson from 2.13.0 to 2.13.1
Bumps [com.google.code.gson:gson](https://github.com/google/gson) from 2.13.0 to 2.13.1.
- [Release notes](https://github.com/google/gson/releases)
- [Changelog](https://github.com/google/gson/blob/main/CHANGELOG.md)
- [Commits](https://github.com/google/gson/compare/gson-parent-2.13.0...gson-parent-2.13.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-14 03:27:17 +08:00
renovate[bot] ed3a68af1e fix(deps): update kotlin monorepo to v2.1.21 2025-05-14 03:25:58 +08:00
dependabot[bot] 447b82c880 build(deps): bump org.mongodb:mongodb-driver-sync from 5.4.0 to 5.5.0
Bumps [org.mongodb:mongodb-driver-sync](https://github.com/mongodb/mongo-java-driver) from 5.4.0 to 5.5.0.
- [Release notes](https://github.com/mongodb/mongo-java-driver/releases)
- [Commits](https://github.com/mongodb/mongo-java-driver/compare/r5.4.0...r5.5.0)

---
updated-dependencies:
- dependency-name: org.mongodb:mongodb-driver-sync
  dependency-version: 5.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-14 03:25:49 +08:00
carm 5700f8c1c6 Merge remote-tracking branch 'origin/master' 2025-04-18 00:53:01 +08:00
carm 1cf230d6b6 chore: Bump to 4.1.4 2025-04-18 00:52:51 +08:00
carm 1ffd4b2f0b build(checks): Bye codacy. 2025-04-18 00:31:42 +08:00
dependabot[bot] 92ce780d6f build(deps): bump kotlin.version from 1.9.22 to 2.1.20
Bumps `kotlin.version` from 1.9.22 to 2.1.20.

Updates `org.jetbrains.kotlin:kotlin-stdlib-jdk8` from 1.9.22 to 2.1.20
- [Release notes](https://github.com/JetBrains/kotlin/releases)
- [Changelog](https://github.com/JetBrains/kotlin/blob/master/ChangeLog.md)
- [Commits](https://github.com/JetBrains/kotlin/compare/v1.9.22...v2.1.20)

Updates `org.jetbrains.kotlin:kotlin-maven-plugin` from 1.9.22 to 2.1.20

---
updated-dependencies:
- dependency-name: org.jetbrains.kotlin:kotlin-stdlib-jdk8
  dependency-version: 2.1.20
  dependency-type: direct:production
  update-type: version-update:semver-major
- dependency-name: org.jetbrains.kotlin:kotlin-maven-plugin
  dependency-version: 2.1.20
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-18 00:30:08 +08:00
carm a2de3303e8 feat(section): Add more section functions 2025-04-17 23:55:15 +08:00
renovate[bot] 47a1981002 fix(deps): update dependency com.google.code.gson:gson to v2.13.0 2025-04-17 22:06:46 +08:00
renovate[bot] b97bc5d1a9 chore(deps): update dependency org.apache.maven.plugins:maven-surefire-plugin to v3.5.3 2025-04-17 22:05:43 +08:00
carm cc927fdc0e Merge pull request #134 from huanmeng-qwq/language/kotlin
feat: Support kotlin dsl
2025-04-17 22:05:28 +08:00
carm 5dc2693106 build(doc): Removed javadoc generation for demo project. 2025-04-17 18:44:48 +08:00
carm 472ce66ca7 build(kotlin): Fixed version 2025-04-17 18:13:00 +08:00
carm 796771554f Merge branch 'master' into language/kotlin 2025-04-17 17:48:38 +08:00
carm 28cb5d4c83 refactor(demo): Move all tests to demo. 2025-04-17 17:43:29 +08:00
carm a670aec986 fix(deps): Fixed the missing kotlin version. 2025-03-30 22:48:19 +08:00
renovate[bot] 51c54b9b53 fix(deps): update dependency org.mongodb:mongodb-driver-sync to v5.4.0 2025-03-23 01:10:53 +08:00
huanmeng-qwq fa99385ff0 feat: Support kotlin dsl 2025-03-22 17:43:48 +08:00
carm 70a3b893a6 chore(text): Add missing text functions 2025-03-21 03:30:47 +08:00
carm c32a941906 docs: Edit logo size 2025-03-21 02:32:33 +08:00
carm 8b597d10f9 feat: Optimized ContentHandler with Replacer & Inserter. 2025-03-21 02:31:04 +08:00
carm cedfcfd262 docs: Update banners and icons 2025-03-21 02:30:35 +08:00
carm 12add75232 feat: Enhanced text content replacer 2025-03-20 20:28:33 +08:00
carm 4d29a24768 docs: Edited readme 2025-03-20 17:16:33 +08:00
carm 8626b86961 chore: Change logo 2025-03-20 16:08:17 +08:00
carm 754dc1de1f feat: Change logo 2025-03-20 16:04:50 +08:00
carm 3d1d4ca609 chore: Add logo & icon. 2025-03-20 15:32:27 +08:00
carm 990b10b6f0 chore: Add logo & icon. 2025-03-20 15:30:34 +08:00
carm 7462cd720e chore: Optimize code format & functions. 2025-03-19 18:36:08 +08:00
carm 6bc83eb3b3 chore: Optimize code format & functions. 2025-03-19 18:35:45 +08:00
carm dbec551c84 chore: Fixed the param errors. 2025-03-17 01:39:08 +08:00
carm 82cca5eca2 feat(exception): Supported ConfigExceptionHandler for holders. 2025-03-17 01:06:57 +08:00
carm 65f3cc1b3d feat(validator): Support validators for config values.
BREAKING CHANGE: `ValueManifest` and `ConfigValue` added a new type "UNIT" to mark the minimal unit value of this instance.

link #132
2025-03-17 00:26:47 +08:00
carm fae048dd69 feat(validator): Support validators for config values.
BREAKING CHANGE: `ValueManifest` and `ConfigValue` added a new type "UNIT" to mark the minimal unit value of this instance.

link #132
2025-03-16 23:56:36 +08:00
carm 854e3df49f feat: Upgrade configured to v4.1.0 2025-03-15 01:52:52 +08:00
carm f6167c3b5e docs: Update license [skip ci] 2025-03-15 01:51:07 +08:00
carm ffe3e88b3b docs: Translated to English 2025-03-14 19:34:06 +08:00
carm caa3077f48 chore: Add missing @Nullable 2025-03-14 19:33:30 +08:00
carm 05dbf0b504 docs: Redesign readme 2025-03-14 19:20:31 +08:00
carm 810e95198e docs: Redesign readme 2025-03-14 19:17:26 +08:00
carm 10004f16b4 docs(logo): Add project banner & logo 2025-03-14 19:09:43 +08:00
carm 8e19748c7c feat(map): Add more builder functions 2025-03-12 04:04:00 +08:00
carm 17762a2e70 refactor(proj): Change project name to "configured" 2025-03-12 03:27:35 +08:00
carm 7dbd607a3f refactor(proj): Change project name to "configured" 2025-03-12 03:22:40 +08:00
carm 035e8a227e test(parse): Add more tests 2025-03-06 20:12:33 +08:00
carm 04eaf6606d feat: Jump to 4.0.11 2025-03-06 18:19:25 +08:00
carm e558e93410 feat(sql): Support custom sql table schema 2025-03-06 18:19:06 +08:00
flowerinsnow e55fe3a8d5 feat: hocon supported 2025-03-06 18:03:27 +08:00
carm 8f075b99b5 chore(deps): use parent dependencies' version 2025-03-04 11:58:01 +08:00
carm 09a0b3c373 chore(deps): use parent dependencies' version 2025-03-04 11:34:16 +08:00
carm 4df4977733 feat(msg): add enhanced text support 2025-03-04 04:21:58 +08:00
carm 6434feb980 fix(sql): Fixed sql table format 2025-03-04 01:57:04 +08:00
carm bc3e4b3e6f fix(comment): Fixed yaml comments 2025-03-04 01:24:12 +08:00
carm c2a9e2254c feat(section): Add ConfigureSection#asMap function 2025-03-04 01:06:21 +08:00
carm 251dd208af feat(sql): Support SQL sources 2025-03-03 19:00:28 +08:00
carm afd1bbfc0f feat(sql): Support SQL sources 2025-03-03 18:52:41 +08:00
carm a699f6c164 feat(sql): Support SQL sources 2025-03-03 18:51:21 +08:00
carm 844cbfab53 feat(sql): Try to implement sql source 2025-02-27 13:32:12 +08:00
carm f74d5d29f9 feat(mongo): Finished source for MongoDB #105 2025-02-27 00:46:44 +08:00
carm 6f28abebb9 feat(section): Add #path and #fullPath for sections 2025-02-25 00:04:24 +08:00
LSeng 842cd78ce3 feat(section): Implement createSection() for ShadedSection 2025-02-22 23:56:45 +08:00
carm 11f1f36a15 docs: Change logo file 2025-02-22 10:13:54 +08:00
renovate[bot] ee9f29ba30 chore(deps): update dependency org.apache.maven.plugins:maven-compiler-plugin to v3.14.0 2025-02-22 09:57:51 +08:00
carm f5bccdaad5 chore: Upgraded to 4.0.6 2025-02-22 09:57:24 +08:00
carm f5f70ff69b chore(source): Add more pre implemented functions 2025-02-22 09:56:59 +08:00
carm 3e221740bc feat(section): Implement shaded sections 2025-02-22 09:55:28 +08:00
LSeng 96d09be977 feat(section): support ShadedSection 2025-02-21 20:48:37 +08:00
carm 5d7c946db5 feat(section): Implement more sections 2025-02-21 18:45:15 +08:00
carm 9e008ff4cd feat(section): Add shaded section 2025-02-21 17:31:05 +08:00
carm df19da170b feat(section): Add #contains and #containsValue methods 2025-02-21 17:15:16 +08:00
carm 3473ef2247 feat: Implement more sections functions 2025-02-21 16:17:12 +08:00
carm d543530305 feat: Implement more sections functions 2025-02-21 16:09:58 +08:00
carm d81855697c feat: Implement more sections functions 2025-02-21 11:27:03 +08:00
carm 5c16e98f30 feat(section): Implement more sections 2025-02-21 03:20:11 +08:00
carm 3f1ffadeff feat(section): Implement more sections 2025-02-21 03:07:43 +08:00
carm d8191b7c6d feat(section): Add original method 2025-02-21 02:00:36 +08:00
carm ba66220f92 docs(sponsor): Add sponsors banner 2025-02-21 01:00:27 +08:00
carm 730d6d7e9c docs(sponsor): Add sponsors banner 2025-02-21 00:58:32 +08:00
carm 4523190cb0 feat: Upgraded to 4.0.5 2025-02-20 16:50:07 +08:00
carm 2a49e2ee6b docs: Fixed quantity usage 2025-02-20 16:49:36 +08:00
carm 77b223b2cb feat: Add stream functions 2025-02-20 16:38:49 +08:00
carm 8e7ac263e7 feat: Split MapSection and MemorySection 2025-02-20 03:01:45 +08:00
carm 80f03ec501 feat: Split MapSection and MemorySection 2025-02-20 02:52:19 +08:00
carm 1c002ae535 feat: Split MapSection and MemorySection 2025-02-20 02:50:00 +08:00
carm a4659c5c9f docs: Englished documents 2025-02-20 00:11:06 +08:00
carm 1c27c2f881 fix: Fixed the missing implements of #remove 2025-02-19 00:57:17 +08:00
carm 476cb79e2f feat: Support standard "pre-load" option 2025-02-18 21:47:58 +08:00
carm bf716b06ae docs: Add more javadocs 2025-02-18 17:33:28 +08:00
carm 00170e6d77 feat: Simplify the ConfigureSection functions to support more different usages. 2025-02-18 17:08:12 +08:00
carm 1bac201427 chore: Add more limitations for inline comments 2025-02-18 14:12:28 +08:00
carm cd77639df5 docs: Add examples 2025-02-17 18:37:16 +08:00
185 changed files with 8030 additions and 2423 deletions
+5 -8
View File
@@ -1,9 +1,6 @@
# EasyConfiguration Javadoc
# configured Javadoc
基于 [Github Pages](https://pages.github.com/) 搭建,请访问 [JavaDoc](https://carmjos.github.io/EasyConfiguration) 。
## 如何实现?
若您也想通过 [Github Actions](https://docs.github.com/en/actions/learn-github-actions)
自动部署项目的Javadoc到 [Github Pages](https://pages.github.com/)
可以参考我的文章 [《自动部署Javadoc到Github Pages》](https://pages.carm.cc/doc/javadoc-in-github.html) 。
Based
on [Github Pages](https://pages.github.com/),
please
see [JavaDoc](https://carmjos.github.io/configured) 。
+2 -15
View File
@@ -1,16 +1,3 @@
# 欢迎使用 EasyConfiguration
# Documentation
这个项目刚刚创建,详细的Javadoc与开发指南还在补充,请给我一点时间~
## 基本定义
Value: 实际配置的单例值。
Manifest: 用于描述值基本配置的对象。
Provider: 用于提供配置文件的接口。
Wrapper: 用于包装配置文件的接口。
Initializer: 用于初始化的接口
See [wiki](https://github.com/CarmJos/configured/wiki).
Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

+346
View File
@@ -0,0 +1,346 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
indent_style = space
tab_width = 4
[*.java]
indent_size = 4
max_line_length = 120
ij_java_align_consecutive_assignments = false
ij_java_align_consecutive_variable_declarations = false
ij_java_align_group_field_declarations = false
ij_java_align_multiline_annotation_parameters = false
ij_java_align_multiline_array_initializer_expression = false
ij_java_align_multiline_assignment = false
ij_java_align_multiline_binary_operation = false
ij_java_align_multiline_chained_methods = false
ij_java_align_multiline_deconstruction_list_components = true
ij_java_align_multiline_extends_list = false
ij_java_align_multiline_for = true
ij_java_align_multiline_method_parentheses = false
ij_java_align_multiline_parameters = true
ij_java_align_multiline_parameters_in_calls = false
ij_java_align_multiline_parenthesized_expression = false
ij_java_align_multiline_records = true
ij_java_align_multiline_resources = true
ij_java_align_multiline_ternary_operation = false
ij_java_align_multiline_text_blocks = false
ij_java_align_multiline_throws_list = false
ij_java_align_subsequent_simple_methods = false
ij_java_align_throws_keyword = false
ij_java_align_types_in_multi_catch = true
ij_java_annotation_new_line_in_record_component = false
ij_java_annotation_parameter_wrap = off
ij_java_array_initializer_new_line_after_left_brace = false
ij_java_array_initializer_right_brace_on_new_line = false
ij_java_array_initializer_wrap = off
ij_java_assert_statement_colon_on_next_line = false
ij_java_assert_statement_wrap = off
ij_java_assignment_wrap = off
ij_java_binary_operation_sign_on_next_line = false
ij_java_binary_operation_wrap = off
ij_java_blank_lines_after_anonymous_class_header = 0
ij_java_blank_lines_after_class_header = 0
ij_java_blank_lines_after_imports = 1
ij_java_blank_lines_after_package = 1
ij_java_blank_lines_around_class = 1
ij_java_blank_lines_around_field = 0
ij_java_blank_lines_around_field_in_interface = 0
ij_java_blank_lines_around_field_with_annotations = 0
ij_java_blank_lines_around_initializer = 1
ij_java_blank_lines_around_method = 1
ij_java_blank_lines_around_method_in_interface = 1
ij_java_blank_lines_before_class_end = 0
ij_java_blank_lines_before_imports = 1
ij_java_blank_lines_before_method_body = 0
ij_java_blank_lines_before_package = 0
ij_java_blank_lines_between_record_components = 0
ij_java_block_brace_style = end_of_line
ij_java_block_comment_add_space = false
ij_java_block_comment_at_first_column = true
ij_java_builder_methods =
ij_java_call_parameters_new_line_after_left_paren = false
ij_java_call_parameters_right_paren_on_new_line = false
ij_java_call_parameters_wrap = off
ij_java_case_statement_on_separate_line = true
ij_java_catch_on_new_line = false
ij_java_class_annotation_wrap = split_into_lines
ij_java_class_brace_style = end_of_line
ij_java_class_count_to_use_import_on_demand = 5
ij_java_class_names_in_javadoc = 1
ij_java_deconstruction_list_wrap = normal
ij_java_do_not_indent_top_level_class_members = false
ij_java_do_not_wrap_after_single_annotation = false
ij_java_do_not_wrap_after_single_annotation_in_parameter = false
ij_java_do_while_brace_force = never
ij_java_doc_add_blank_line_after_description = true
ij_java_doc_add_blank_line_after_param_comments = false
ij_java_doc_add_blank_line_after_return = false
ij_java_doc_add_p_tag_on_empty_lines = true
ij_java_doc_align_exception_comments = true
ij_java_doc_align_param_comments = true
ij_java_doc_do_not_wrap_if_one_line = false
ij_java_doc_enable_formatting = true
ij_java_doc_enable_leading_asterisks = true
ij_java_doc_indent_on_continuation = false
ij_java_doc_keep_empty_lines = true
ij_java_doc_keep_empty_parameter_tag = true
ij_java_doc_keep_empty_return_tag = true
ij_java_doc_keep_empty_throws_tag = true
ij_java_doc_keep_invalid_tags = true
ij_java_doc_param_description_on_new_line = false
ij_java_doc_preserve_line_breaks = false
ij_java_doc_use_throws_not_exception_tag = true
ij_java_else_on_new_line = false
ij_java_entity_dd_prefix =
ij_java_entity_dd_suffix = EJB
ij_java_entity_eb_prefix =
ij_java_entity_eb_suffix = Bean
ij_java_entity_hi_prefix =
ij_java_entity_hi_suffix = Home
ij_java_entity_lhi_prefix = Local
ij_java_entity_lhi_suffix = Home
ij_java_entity_li_prefix = Local
ij_java_entity_li_suffix =
ij_java_entity_pk_class = java.lang.String
ij_java_entity_ri_prefix =
ij_java_entity_ri_suffix =
ij_java_entity_vo_prefix =
ij_java_entity_vo_suffix = VO
ij_java_enum_constants_wrap = off
ij_java_enum_field_annotation_wrap = off
ij_java_extends_keyword_wrap = off
ij_java_extends_list_wrap = off
ij_java_field_annotation_wrap = split_into_lines
ij_java_field_name_prefix =
ij_java_field_name_suffix =
ij_java_filter_class_prefix =
ij_java_filter_class_suffix =
ij_java_filter_dd_prefix =
ij_java_filter_dd_suffix =
ij_java_finally_on_new_line = false
ij_java_for_brace_force = never
ij_java_for_statement_new_line_after_left_paren = false
ij_java_for_statement_right_paren_on_new_line = false
ij_java_for_statement_wrap = off
ij_java_generate_final_locals = false
ij_java_generate_final_parameters = false
ij_java_generate_use_type_annotation_before_type = true
ij_java_if_brace_force = never
ij_java_imports_layout = @*, *, |, javax.**, java.**, |, $*
ij_java_indent_case_from_switch = true
ij_java_insert_inner_class_imports = false
ij_java_insert_override_annotation = true
ij_java_keep_blank_lines_before_right_brace = 2
ij_java_keep_blank_lines_between_package_declaration_and_header = 2
ij_java_keep_blank_lines_in_code = 2
ij_java_keep_blank_lines_in_declarations = 2
ij_java_keep_builder_methods_indents = false
ij_java_keep_control_statement_in_one_line = true
ij_java_keep_first_column_comment = true
ij_java_keep_indents_on_empty_lines = false
ij_java_keep_line_breaks = true
ij_java_keep_multiple_expressions_in_one_line = false
ij_java_keep_simple_blocks_in_one_line = false
ij_java_keep_simple_classes_in_one_line = false
ij_java_keep_simple_lambdas_in_one_line = false
ij_java_keep_simple_methods_in_one_line = false
ij_java_label_indent_absolute = false
ij_java_label_indent_size = 0
ij_java_lambda_brace_style = end_of_line
ij_java_layout_on_demand_import_from_same_package_first = true
ij_java_layout_static_imports_separately = true
ij_java_line_comment_add_space = false
ij_java_line_comment_add_space_on_reformat = false
ij_java_line_comment_at_first_column = true
ij_java_listener_class_prefix =
ij_java_listener_class_suffix =
ij_java_local_variable_name_prefix =
ij_java_local_variable_name_suffix =
ij_java_message_dd_prefix =
ij_java_message_dd_suffix = EJB
ij_java_message_eb_prefix =
ij_java_message_eb_suffix = Bean
ij_java_method_annotation_wrap = split_into_lines
ij_java_method_brace_style = end_of_line
ij_java_method_call_chain_wrap = off
ij_java_method_parameters_new_line_after_left_paren = false
ij_java_method_parameters_right_paren_on_new_line = false
ij_java_method_parameters_wrap = off
ij_java_modifier_list_wrap = false
ij_java_multi_catch_types_wrap = normal
ij_java_names_count_to_use_import_on_demand = 3
ij_java_new_line_after_lparen_in_annotation = false
ij_java_new_line_after_lparen_in_deconstruction_pattern = true
ij_java_new_line_after_lparen_in_record_header = false
ij_java_new_line_when_body_is_presented = false
ij_java_packages_to_use_import_on_demand = java.awt.*, javax.swing.*
ij_java_parameter_annotation_wrap = off
ij_java_parameter_name_prefix =
ij_java_parameter_name_suffix =
ij_java_parentheses_expression_new_line_after_left_paren = false
ij_java_parentheses_expression_right_paren_on_new_line = false
ij_java_place_assignment_sign_on_next_line = false
ij_java_prefer_longer_names = true
ij_java_prefer_parameters_wrap = false
ij_java_preserve_module_imports = true
ij_java_record_components_wrap = normal
ij_java_repeat_annotations =
ij_java_repeat_synchronized = true
ij_java_replace_instanceof_and_cast = false
ij_java_replace_null_check = true
ij_java_replace_sum_lambda_with_method_ref = true
ij_java_resource_list_new_line_after_left_paren = false
ij_java_resource_list_right_paren_on_new_line = false
ij_java_resource_list_wrap = off
ij_java_rparen_on_new_line_in_annotation = false
ij_java_rparen_on_new_line_in_deconstruction_pattern = true
ij_java_rparen_on_new_line_in_record_header = false
ij_java_servlet_class_prefix =
ij_java_servlet_class_suffix =
ij_java_servlet_dd_prefix =
ij_java_servlet_dd_suffix =
ij_java_session_dd_prefix =
ij_java_session_dd_suffix = EJB
ij_java_session_eb_prefix =
ij_java_session_eb_suffix = Bean
ij_java_session_hi_prefix =
ij_java_session_hi_suffix = Home
ij_java_session_lhi_prefix = Local
ij_java_session_lhi_suffix = Home
ij_java_session_li_prefix = Local
ij_java_session_li_suffix =
ij_java_session_ri_prefix =
ij_java_session_ri_suffix =
ij_java_session_si_prefix =
ij_java_session_si_suffix = Service
ij_java_space_after_closing_angle_bracket_in_type_argument = false
ij_java_space_after_colon = true
ij_java_space_after_comma = true
ij_java_space_after_comma_in_type_arguments = true
ij_java_space_after_for_semicolon = true
ij_java_space_after_quest = true
ij_java_space_after_type_cast = true
ij_java_space_before_annotation_array_initializer_left_brace = false
ij_java_space_before_annotation_parameter_list = false
ij_java_space_before_array_initializer_left_brace = false
ij_java_space_before_catch_keyword = true
ij_java_space_before_catch_left_brace = true
ij_java_space_before_catch_parentheses = true
ij_java_space_before_class_left_brace = true
ij_java_space_before_colon = true
ij_java_space_before_colon_in_foreach = true
ij_java_space_before_comma = false
ij_java_space_before_deconstruction_list = false
ij_java_space_before_do_left_brace = true
ij_java_space_before_else_keyword = true
ij_java_space_before_else_left_brace = true
ij_java_space_before_finally_keyword = true
ij_java_space_before_finally_left_brace = true
ij_java_space_before_for_left_brace = true
ij_java_space_before_for_parentheses = true
ij_java_space_before_for_semicolon = false
ij_java_space_before_if_left_brace = true
ij_java_space_before_if_parentheses = true
ij_java_space_before_method_call_parentheses = false
ij_java_space_before_method_left_brace = true
ij_java_space_before_method_parentheses = false
ij_java_space_before_opening_angle_bracket_in_type_parameter = false
ij_java_space_before_quest = true
ij_java_space_before_switch_left_brace = true
ij_java_space_before_switch_parentheses = true
ij_java_space_before_synchronized_left_brace = true
ij_java_space_before_synchronized_parentheses = true
ij_java_space_before_try_left_brace = true
ij_java_space_before_try_parentheses = true
ij_java_space_before_type_parameter_list = false
ij_java_space_before_while_keyword = true
ij_java_space_before_while_left_brace = true
ij_java_space_before_while_parentheses = true
ij_java_space_inside_one_line_enum_braces = false
ij_java_space_within_empty_array_initializer_braces = false
ij_java_space_within_empty_method_call_parentheses = false
ij_java_space_within_empty_method_parentheses = false
ij_java_spaces_around_additive_operators = true
ij_java_spaces_around_annotation_eq = true
ij_java_spaces_around_assignment_operators = true
ij_java_spaces_around_bitwise_operators = true
ij_java_spaces_around_equality_operators = true
ij_java_spaces_around_lambda_arrow = true
ij_java_spaces_around_logical_operators = true
ij_java_spaces_around_method_ref_dbl_colon = false
ij_java_spaces_around_multiplicative_operators = true
ij_java_spaces_around_relational_operators = true
ij_java_spaces_around_shift_operators = true
ij_java_spaces_around_type_bounds_in_type_parameters = true
ij_java_spaces_around_unary_operator = false
ij_java_spaces_inside_block_braces_when_body_is_present = false
ij_java_spaces_within_angle_brackets = false
ij_java_spaces_within_annotation_parentheses = false
ij_java_spaces_within_array_initializer_braces = false
ij_java_spaces_within_braces = false
ij_java_spaces_within_brackets = false
ij_java_spaces_within_cast_parentheses = false
ij_java_spaces_within_catch_parentheses = false
ij_java_spaces_within_deconstruction_list = false
ij_java_spaces_within_for_parentheses = false
ij_java_spaces_within_if_parentheses = false
ij_java_spaces_within_method_call_parentheses = false
ij_java_spaces_within_method_parentheses = false
ij_java_spaces_within_parentheses = false
ij_java_spaces_within_record_header = false
ij_java_spaces_within_switch_parentheses = false
ij_java_spaces_within_synchronized_parentheses = false
ij_java_spaces_within_try_parentheses = false
ij_java_spaces_within_while_parentheses = false
ij_java_special_else_if_treatment = true
ij_java_static_field_name_prefix =
ij_java_static_field_name_suffix =
ij_java_subclass_name_prefix =
ij_java_subclass_name_suffix = Impl
ij_java_switch_expressions_wrap = normal
ij_java_ternary_operation_signs_on_next_line = false
ij_java_ternary_operation_wrap = off
ij_java_test_name_prefix =
ij_java_test_name_suffix = Test
ij_java_throws_keyword_wrap = off
ij_java_throws_list_wrap = off
ij_java_use_external_annotations = false
ij_java_use_fq_class_names = false
ij_java_use_relative_indents = false
ij_java_use_single_class_imports = true
ij_java_variable_annotation_wrap = off
ij_java_visibility = public
ij_java_while_brace_force = never
ij_java_while_on_new_line = false
ij_java_wrap_comments = false
ij_java_wrap_first_method_in_call_chain = false
ij_java_wrap_long_lines = false
ij_java_wrap_semicolon_after_call_chain = false
[*.xml]
indent_style = space
indent_size = 4
tab_width = 4
max_line_length = 120
[*.json]
indent_style = space
indent_size = 2
tab_width = 2
max_line_length = 120
[*.{yml,yaml}]
indent_style = space
indent_size = 2
tab_width = 2
max_line_length = 120
[*.md]
max_line_length = 500
trim_trailing_whitespace = false
-1
View File
@@ -1,2 +1 @@
github: [ CarmJos ]
custom: [ 'https://donate.carm.cc' ]
+20 -23
View File
@@ -1,37 +1,34 @@
---
name: 问题提交
about: 描述问题并提交,帮助我们对其进行检查与修复。
title: ''
labels: bug
name: Submit bugs&issues
about: Describe the problem and submit it to help us review and fix it.
title: 'fix: '
labels: bug
assignees: ''
---
### **问题简述**
### **Description**
用简短的话语描述一下大概问题。
<!-- Describe the general problem in short words.-->
### **问题来源**
### **Operations**
描述一下通过哪些操作才发现的问题,如:
<!--
Describe the problem discovered through what operations, such as:
1. 使用了 '...'
2. 输入了 '....'
3. 出现了报错 '....'
1. Clicked '...'
2. Typed '....'
3. Error says '....'
-->
### **预期结果** (可选)
### **Expected result** _(Optional)_
如果问题不发生,应该是什么情况
### **Screenshots & Error Logs**
### **问题截图/问题报错**
### **Environment**
如果有报错或输出,请提供截图。
- System: `Windows 10` / `Ubuntu` / `...`
- Java version: `JDK11` / `OPENJDK8` / `JRE8` / `...`
### **操作环境**
### **Anything else...**
- 系统环境: `Windows 10` / `Ubuntu` / `...`
- Java版本: `JDK11` / `OPENJDK8` / `JRE8` / `...`
### **其他补充**
如有其他补充,可以在这里描述。
<!-- If there are other supplements, they can be described here. -->
+11 -11
View File
@@ -1,23 +1,23 @@
---
name: 功能需求
about: 希望我们提供更多的功能。
title: ''
name: Features
about: Ask for new features.
title: 'feat: '
labels: enhancement
assignees: ''
---
### **功能简述**
### **Description**
简单的描述一下你想要的功能
<!-- Describe the features in short words.-->
### **需求来源**
### **Source**
简单的描述一下为什么需要这个功能。
<!-- Describe the reason that you need this feature in short words.-->
### **功能参考**(可选)
### **Examples** _(Optional)_
如果有相关功能的参考,如文本、截图,请提供给我们。
<!--Any screenshots or example codes please.-->
### **附加内容**
### **Additional details**
如果有什么小细节需要重点注意,请在这里告诉我们。
<!--If there are any small details that need to be highlighted, please let us know here.-->
-54
View File
@@ -1,54 +0,0 @@
# 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@v4
# 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@09916000460adeeedc96b9704f86deba53e2ad5d
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@v3
with:
sarif_file: results.sarif
+4 -4
View File
@@ -38,11 +38,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,7 +53,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
uses: github/codeql-action/autobuild@v4
# ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -67,4 +67,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
uses: github/codeql-action/analyze@v4
+9 -9
View File
@@ -16,11 +16,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: "Set up JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '11'
java-version: '21'
distribution: 'adopt'
cache: maven
server-id: github
@@ -47,7 +47,7 @@ jobs:
id: sitemap
uses: cicirello/generate-sitemap@v1
with:
base-url-path: https://CarmJos.github.io/EasyConfiguration
base-url-path: https://CarmJos.github.io/configured
path-to-root: docs
- name: "Output stats"
@@ -72,7 +72,7 @@ jobs:
run: |
cd docs
git init
git remote add origin git@github.com:CarmJos/EasyConfiguration.git
git remote add origin git@github.com:CarmJos/configured.git
git checkout -b gh-pages
git add -A
git commit -m "API Document generated."
@@ -87,11 +87,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: "Set up JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '11'
java-version: '21'
distribution: 'adopt'
cache: maven
server-id: central
@@ -105,4 +105,4 @@ jobs:
env:
MAVEN_USERNAME: ${{ secrets.OSSRH_USER }}
MAVEN_PASSWORD: ${{ secrets.OSSRH_PASS }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
+4 -4
View File
@@ -15,18 +15,18 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: "Set up JDK"
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
java-version: '11'
java-version: '21'
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@v4
uses: actions/upload-artifact@v7
with:
name: Artifact
path: staging
+2 -1
View File
@@ -1,3 +1,4 @@
.idea/
.idea/*
!.idea/icon.svg
**/target/
**.iml
+3
View File
@@ -0,0 +1,3 @@
[submodule ".wiki"]
path = .wiki
url = https://github.com/CarmJos/configured.wiki.git
+36
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

Submodule
+1
Submodule .wiki added at c79c6f4a43
+75
View File
@@ -0,0 +1,75 @@
# Code of Conduct
(English primary version a brief Chinese note may follow. 中文提示:本文件英文为主,若理解存在困难可联系维护者。)
This project adopts the principles of the Contributor Covenant v2.1 (https://www.contributor-covenant.org/version/2/1/code_of_conduct/) with projectspecific clarifications below. By participating you agree to uphold this Code.
## Our Pledge
We strive to provide a harassmentfree, inclusive, friendly, and productive environment for everyone, regardless of age, body, disability, ethnicity, sex characteristics, gender identity or expression, level of experience, education, socioeconomic status, nationality, personal appearance, race, caste, religion (or lack thereof), sexual identity or orientation, or technical choices.
## Our Standards
Examples of behavior that contributes to a positive environment:
- Showing empathy and respect to all participants
- Giving and gracefully accepting constructive feedback
- Focusing on what is best for the project and community
- Being honest about mistakes and seeking improvement
- Using welcoming and inclusive language
Examples of unacceptable behavior include:
- Harassment, discrimination, or derogatory comments
- Trolling, insulting or antagonistic remarks, personal or political attacks
- Public or private harassment or sustained disruption of activities
- Publishing others private information without explicit permission
- Sexualized language, imagery, or unwelcome advances
- Any other conduct reasonably deemed inappropriate or unsafe
## Scope
This Code applies within all project spaces (repository code, issues, pull requests, discussions, wiki, CI logs) and in public spaces whenever an individual is representing the project or community.
## Responsibilities & Enforcement
Project maintainers ("maintainers") are responsible for clarifying standards and taking appropriate, fair, and consistent corrective action.
Maintainers may remove or edit contributions that violate this Code (comments, commits, code, wiki edits, issues, discussions) and may temporarily or permanently ban any contributor for abusive, harassing, or otherwise harmful behavior.
## Reporting
Report violations privately via:
- Email: carm@carm.cc
Please include (if possible):
- Links, timestamps, or message IDs
- Description of what happened and why it is a concern
- Screenshots or logs (if relevant)
- Preferred or suggested resolution
We aim to acknowledge reports within 72 hours and provide an initial assessment within 7 days. All goodfaith reports will be treated confidentially and only shared with individuals who need the information to resolve the issue.
## Enforcement Guidelines
Maintainers will use these guidelines to determine the impact of an incident and consequences:
1. Correction
- Impact: Use of inappropriate language or other unprofessional conduct.
- Consequence: Private or public warning, request for change.
2. Warning
- Impact: A single severe incident or repeated inappropriate behavior.
- Consequence: Official warning; continued misconduct leads to escalation.
3. Temporary Suspension
- Impact: Persistent violations despite previous warnings.
- Consequence: Temporary participation suspension (issues/PRs/discussions). Conditions for reinstatement communicated.
4. Permanent Ban
- Impact: Demonstrated pattern of harassment, hate, or threats; refusal to reform.
- Consequence: Permanent removal from community spaces.
## Conflicts of Interest
Maintainers will recuse themselves from enforcement decisions where they have a personal conflict. A neutral maintainer or external trusted community member may be asked to assist when appropriate.
## Appeals
If you believe an enforcement action was made in error or was unfair, you may appeal by emailing the same reporting address within 14 days, providing context and justification.
## Attribution
Portions adapted from Contributor Covenant v2.1 and other open source community best practices.
## Changes & Versioning
Substantive changes to this Code will be announced in the repository (Release Notes / CHANGELOG / Discussions). Historical versions will remain accessible via Git history.
---
If unsure whether something is acceptable: choose respect, transparency, and ask a maintainer before acting.
+210
View File
@@ -0,0 +1,210 @@
# Contributing Guide
> English is the primary language. A brief Chinese hint: 若需中文协助可在 Issue 中说明。
Thank you for investing time in contributing! This document describes how to propose changes and how we maintain quality and consistency across the project.
## Quick Links
- Code of Conduct: ./CODE_OF_CONDUCT.md
- Security Policy: ./SECURITY.md
- Issues: https://github.com/CarmJos/configured/issues
- Discussions / Q&A: (open an Issue if Discussions are disabled)
## Table of Contents
1. Principles
2. Scope of Contributions
3. Getting Started (Environment & Build)
4. Project Structure
5. Branching & Workflow
6. Issue Workflow
7. Pull Request Guidelines
8. Commit Message Convention
9. Coding Standards
10. Testing Guidelines
11. Documentation & JavaDoc
12. Dependency Policy
13. Versioning & Releases
14. Performance Expectations
15. Internationalization / Language
16. FAQ for Contributors
17. Attribution
---
## 1. Principles
We value: correctness, clarity, minimalism, maintainability, security-by-default, and performance without premature complexity. Every contribution should move at least one of these forward while not regressing the others.
## 2. Scope of Contributions
Acceptable contributions include (but are not limited to):
- Bug fixes & test coverage improvements
- Performance optimizations with measurable benefit
- New configuration providers (storage backends) with generic value
- Validation or serialization helpers
- Documentation, examples, or tutorials
- Tooling that improves developer productivity or release robustness
Out-of-scope (likely to be declined):
- Vendor lockin features narrowly targeting one proprietary platform (unless optional & isolated)
- Large feature branches without prior design discussion
- Unbounded abstraction that increases complexity with unclear user value
## 3. Getting Started (Environment & Build)
Requirements:
- JDK 8 (minimum). Later JDKs may work but target bytecode is 1.8.
- Maven 3.8+ (Wrapper optional; project assumes standard mvn).
Build all modules:
```bash
mvn -q clean verify
```
Skip tests (NOT recommended for PR validation):
```bash
mvn -q clean install -DskipTests
```
Run a single module:
```bash
mvn -q -pl core -am test
```
Generate JavaDoc (already bound in build):
```bash
mvn -q javadoc:javadoc
```
## 4. Project Structure (High-level)
- core/ : Fundamental abstractions (Configuration, Value types, factories)
- features/ : Optional, orthogonal enhancements (validators, section, text, etc.)
- providers/ : Concrete persistence / parsing backends (yaml, gson, hocon, sql, mongodb, temp)
- demo/ : Usage demonstrations & sample scenarios
Rules:
- Core must not depend on feature or provider modules.
- Features must not form cycles; prefer depending only on core.
- Providers should keep external dependencies minimal and shaded/isolated only if necessary.
## 5. Branching & Workflow
- main (or master): Stable; only fastforward / squash from reviewed PRs.
- feature/<short-name>: New feature work. Open draft PR early for feedback.
- fix/<issue-id>-<slug>: Bug fix referencing an Issue.
- chore/<topic>: Build, infra, docs improvements.
Never force push to main. Force pushes allowed only to your own feature branches.
## 6. Issue Workflow
1. Search existing issues first to avoid duplication.
2. Provide reproduction steps (minimal code or config) for bugs.
3. Label suggestions as enhancement; performance items as perf.
4. For larger features, open a design issue summarizing: Problem, Motivation, Proposed API, Alternatives.
## 7. Pull Request Guidelines
Checklist before opening a PR:
- Linked to at least one Issue (unless trivial doc fix)
- Passes `mvn verify`
- Adds or updates tests covering new behavior / bug
- Includes JavaDoc / README / CHANGELOG fragment if user-facing
- No unrelated refactors or formatting churn
- Minimal diff: avoid reordering imports unless enforced by style
Review expectations:
- Maintainers strive to respond within 5 business days.
- Use constructive, actionoriented comments.
- Resolve conversations or explain why not.
- Squash commits if they are noisy; retain meaningful logical grouping.
## 8. Commit Message Convention
Use Conventional Commits (https://www.conventionalcommits.org/) with optional scope:
```
<type>(<scope>): <short imperative summary>
<body>(optional)
<footer>(breaking changes, issue references)
```
Types used:
- feat: New feature (user visible)
- fix: Bug fix
- perf: Performance improvement
- refactor: Internal restructuring without behavior change
- docs: Documentation only
- test: Add or fix tests
- build: Build system or dependency changes
- ci: Continuous integration changes
- chore: Maintenance tasks
- style: Formatting (rare; avoid large styleonly changes)
Breaking changes: add `!` after type/scope or include `BREAKING CHANGE:` footer.
## 9. Coding Standards
- Java: Follow effective Java principles; prefer explicit types over inference for public APIs.
- Nullability: Use JetBrains annotations (`@NotNull`, `@Nullable`) where helpful.
- Immutability: Favor immutable value objects; avoid exposing mutable internal state.
- Exceptions: Use specific exception types; no swallowing silently. Validate inputs early.
- Logging: Keep core logging minimal; let consumers decide verbosity. Avoid println.
- APIs: Minimize surface; avoid exposing prematurely general interfaces.
- Annotations: Provide meaningful config path / comments metadata clearly.
### Style
- Indentation: 4 spaces.
- Line length guideline: ≤ 140 chars (soft limit).
- Avoid wildcard imports.
## 10. Testing Guidelines
- Use JUnit (current: JUnit 4). Prefer deterministic, isolated tests.
- Each bug fix must include a regression test failing before the fix.
- Avoid timesensitive sleeps; prefer deterministic constructs.
- Keep provider-specific integration tests under provider module.
- Use random data cautiously; if used, log seed for reproduction.
Command:
```bash
mvn -q test
```
## 11. Documentation & JavaDoc
- Public classes & methods: brief JavaDoc describing contract, thread-safety, nullability.
- Add code examples when clarifying complex usage.
- Update README or module README for new feature flags or environment variables.
- Keep demo module aligned with latest recommended usage.
## 12. Dependency Policy
- Keep transitive dependency footprint lean.
- No large frameworks for simple utilities.
- Justify each new dependency in PR description (purpose, size, maintenance risk).
- Prefer stable, well-adopted libraries with permissive licenses compatible with LGPL.
- Security-sensitive libs (parsers, DB drivers) should be periodically updated.
## 13. Versioning & Releases
- Follows Semantic Versioning (MAJOR.MINOR.PATCH).
- Public API additions => MINOR bump.
- Backwards-compatible bug fix => PATCH.
- Backwards-incompatible change => MAJOR (document rationale & migration).
- Release steps (maintainers):
1. Ensure main is green (CI all passing)
2. Update CHANGELOG (if present) or Release Notes draft
3. Bump versions via maven-release-plugin (ensure GPG & staging configured)
4. Tag + push; verify publication (Central / GitHub Packages)
5. Publish GitHub Release with highlights + migration notes
## 14. Performance Expectations
- Avoid unnecessary object churn in hot paths.
- Profile before large rewrites. Provide benchmark or allocation stats if claiming improvement.
- Defer I/O and heavy parsing until needed (lazy loading pattern).
## 15. Internationalization / Language
- Primary language: English for code, comments, issues, PRs.
- Chinese clarifications acceptable if accompanied by English.
## 16. FAQ for Contributors
Q: Can I add a new provider?
A: Yes—open a design Issue first summarizing data model, external dependencies, and test strategy.
Q: How do I mark experimental APIs?
A: Add JavaDoc: `@apiNote Experimental: subject to change without notice.` and avoid wide promotion.
Q: Why Java 8 target?
A: Maximizes compatibility across server & embedded environments.
## 17. Attribution
Portions inspired by widely adopted open-source guidelines (Kotlin, Spring, Apache projects) and Conventional Commits.
---
Thank you for helping build a robust configuration ecosystem!
+1
View File
@@ -1,6 +1,7 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
+87 -56
View File
@@ -1,31 +1,30 @@
```text
____ _____ ____ __ _
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
/___/ /___/
```
<div align=center>
<img src=".doc/images/banner.png" alt="Banner"/>
[![version](https://img.shields.io/github/v/release/CarmJos/configured)](https://github.com/CarmJos/configured/releases)
[![License](https://img.shields.io/github/license/CarmJos/configured)](https://www.gnu.org/licenses/lgpl-3.0.html)
[![workflow](https://github.com/CarmJos/configured/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/configured/actions/workflows/maven.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/carmjos/configured/badge)](https://www.codefactor.io/repository/github/carmjos/configured)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/configured)
README LANGUAGES [ [**English**](README.md) | [中文](README_CN.md) ]
</div>
# EasyConfiguration
# configured _(config framework)_
[![version](https://img.shields.io/github/v/release/CarmJos/EasyConfiguration)](https://github.com/CarmJos/EasyConfiguration/releases)
[![License](https://img.shields.io/github/license/CarmJos/EasyConfiguration)](https://www.gnu.org/licenses/lgpl-3.0.html)
[![workflow](https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/carmjos/easyconfiguration/badge)](https://www.codefactor.io/repository/github/carmjos/easyconfiguration)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/EasyConfiguration)
![](https://visitor-badge.glitch.me/badge?page_id=EasyConfiguration.readme)
<img src=".doc/images/logo-bg.svg" width="150px" alt="logo" align="right" style="float: right"/>
**Easy _(to make)_ Configurations!**
_**"Once set, Simple get."**_
A simple, easy-to-use and universal solution for managing configuration files.
Enjoy the ease of use with customizable formats for loading, reading, and updating your configuration files.
A simple, easy-to-use and universal solution for managing, loading, reading,
and updating configuration files.
Supported **JSON**, **YAML**, **Hocon**, **TOML**, **SQL**, **MongoDB**... and much more!
## Features & Advantages
Supported [YAML](impl/yaml), [JSON](impl/json), [HOCON](impl/hocon) and [SQL](impl/sql) based configuration files
format.
Supported [YAML](providers/yaml), [JSON](providers/json), [HOCON](providers/hocon) and [SQL](providers/sql)
based configuration files format.
- Class-based mechanism for initializing, loading, retrieving, and updating configuration files, ensuring convenience
and efficiency.
@@ -35,56 +34,75 @@ format.
## Development
For the latest JavaDoc release, [CLICK HERE](https://CarmJos.github.io/EasyConfiguration).
For the latest JavaDoc release, [CLICK HERE](https://CarmJos.github.io/configured).
For a detailed development guide, [CLICK HERE](.doc/README.md).
For a detailed development guide, see [wiki](https://github.com/CarmJos/configured/wiki).
### Code Samples
### Preview
To quickly demonstrate the applicability of the project, here are a few practical demonstrations:
- [Database configuration.](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java)
- [Demonstration of all types of configuration instance classes.](demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java)
- [Demonstration of configurations instance classes.](demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java)
Check out all code demonstrations [HERE](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java).
For more examples, see the [Development Guide](.doc/README.md).
```java
@HeaderComment("Configurations for sample")
interface SampleConfig extends Configuration {
@ConfigPath(root = true)
@HeaderComments("Configurations for sample")
public interface SampleConfig extends Configuration {
@InlineComment("Enabled?") // Inline comment
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
@HeaderComments("Server configurations") // Header comment
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class);
@HeaderComments({"[ UUID >-----------------------------------", "A lot of UUIDs"})
@FooterComments("[ UUID >-----------------------------------")
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
.parseValue(UUID::fromString).serializeValue(UUID::toString)
.parse(UUID::fromString).serialize(UUID::toString)
.defaults(
UUID.fromString("00000000-0000-0000-0000-000000000000"),
UUID.fromString("00000000-0000-0000-0000-000000000001")
).build();
@ConfigPath("info") // Custom path
interface INFO extends Configuration {
@HeaderComment("Configure your name!") // Header comment
@HeaderComments("Configure your name!") // Header comment
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
@ConfigPath("year") // Custom path
@ConfigPath("how-old-are-you") // Custom path
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
}
}
```
```java
public class Sample {
public static void main(String[] args) {
// 1. Make a configuration provider from a file.
ConfigurationProvider<?> provider = EasyConfiguration.from("config.yml");
// 2. Initialize the configuration classes or instances.
provider.initialize(SampleConfig.class);
// 3. Enjoy using the configuration!
SampleConfig.ENABLED.set(false);
System.out.println("Your name is " + SampleConfig.INFO.NAME.getNotNull() + " !");
}
public static void main(String[] args) {
// 1. Make a configuration provider from a file.
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/config.yml")
.resourcePath("configs/sample.yml")
.indent(4) // Optional: Set the indentation of the configuration file.
.build();
// 2. Initialize the configuration classes or instances.
holder.initialize(SampleConfig.class);
// 3. Enjoy using the configuration!
System.out.println("Enabled? -> " + SampleConfig.ENABLED.resolve()); // true
SampleConfig.ENABLED.set(false);
System.out.println("And now? -> " + SampleConfig.ENABLED.resolve()); // false
// p.s. Changes not save so enable value will still be true in the next run.
System.out.println("Your name is " + SampleConfig.INFO.NAME.resolve() + " (age=" + SampleConfig.INFO.AGE.resolve() + ")!");
}
}
```
@@ -92,16 +110,22 @@ public class Sample {
```yaml
# Configurations for sample
enabled: true # Enabled?
enabled: true #Enabled?
# Server configurations
port:
# [ UUID >-----------------------------------
# A lot of UUIDs
uuids:
- 00000000-0000-0000-0000-000000000000
- 00000000-0000-0000-0000-000000000001
# [ UUID >-----------------------------------
info:
# Configure your name!
name: Joker
year: 24
how-old-are-you: 24
```
### Dependencies
@@ -125,9 +149,9 @@ info:
<repository>
<!-- Using GitHub dependencies for real-time updates, configuration required (recommended). -->
<id>EasyConfiguration</id>
<id>configured</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
<url>https://maven.pkg.github.com/CarmJos/configured</url>
</repository>
</repositories>
@@ -146,7 +170,7 @@ info:
<!-- Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”. -->
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-core</artifactId>
<artifactId>configured-core</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
@@ -154,15 +178,15 @@ info:
<!-- YAML file-based implementation, compatible with all Java environments. -->
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-yaml</artifactId>
<artifactId>configured-yaml</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
<!-- JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments. -->
<!-- JSON file-based implementation, compatible with all Java environments. -->
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-json</artifactId>
<artifactId>configured-gson</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
@@ -185,7 +209,7 @@ repositories {
mavenCentral()
// Using GitHub dependencies for real-time updates, configuration required (recommended).
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
maven { url 'https://maven.pkg.github.com/CarmJos/configured' }
}
```
@@ -200,13 +224,13 @@ repositories {
dependencies {
// Basic implementation part, requiring custom implementation of “Provider” and “Wrapper”.
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
api "cc.carm.lib:configured-core:[LATEST RELEASE]"
// YAML file-based implementation, compatible with all Java environments.
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
api "cc.carm.lib:configured-yaml:[LATEST RELEASE]"
// JSON file-based implementation, compatible with all Java environments. Note: JSON does not support file comments.
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
// JSON file-based implementation, compatible with all Java environments.
api "cc.carm.lib:configured-gson:[LATEST RELEASE]"
}
```
@@ -217,22 +241,29 @@ dependencies {
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos)
EasyConfiguration for MineCraft!
configured for MineCraft!
Easily manage configurations on MineCraft-related server platforms.
Currently supports BungeeCord, Bukkit (Spigot) servers, with more platforms to be supported soon.
Currently, it supports BungeeCord, Velocity, Bukkit (Spigot) servers,
with more platforms to be supported soon.
## Support and Donation
If you appreciate this plugin, consider supporting me with a donation!
If you appreciate this plugin, consider supporting me with a [donation](https://github.com/sponsors/CarmJos)!
Thank you for supporting open-source projects!
Many thanks to Jetbrains for kindly providing a license for us to work on this and other open-source projects.
[![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/CarmJos/EasyConfiguration)
[![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/CarmJos/configured)
Many thanks to [ArtformGames](https://github.com/ArtformGames) for their
strong support and active contribution to this project!
<img src="https://raw.githubusercontent.com/ArtformGames/.github/master/logo/logo_full.svg" width="317px" height="117px" alt="ArtformGames">
## Open Source License
This project's source code is licensed under
the [GNU LESSER GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/lgpl-3.0.html).
+81 -56
View File
@@ -1,29 +1,27 @@
```text
____ _____ ____ __ _
/ __/__ ____ __ __ / ___/__ ___ / _(_)__ ___ _________ _/ /_(_)__ ___
/ _// _ `(_-</ // / / /__/ _ \/ _ \/ _/ / _ `/ // / __/ _ `/ __/ / _ \/ _ \
/___/\_,_/___/\_, / \___/\___/_//_/_//_/\_, /\_,_/_/ \_,_/\__/_/\___/_//_/
/___/ /___/
```
<div align=center>
<img src=".doc/images/banner.png" alt="Banner"/>
[![version](https://img.shields.io/github/v/release/CarmJos/configured)](https://github.com/CarmJos/configured/releases)
[![License](https://img.shields.io/github/license/CarmJos/configured)](https://www.gnu.org/licenses/lgpl-3.0.html)
[![workflow](https://github.com/CarmJos/configured/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/configured/actions/workflows/maven.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/carmjos/configured/badge)](https://www.codefactor.io/repository/github/carmjos/configured)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/configured)
README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
# EasyConfiguration
</div>
[![version](https://img.shields.io/github/v/release/CarmJos/EasyConfiguration)](https://github.com/CarmJos/EasyConfiguration/releases)
[![License](https://img.shields.io/github/license/CarmJos/EasyConfiguration)](https://www.gnu.org/licenses/lgpl-3.0.html)
[![workflow](https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml/badge.svg?branch=master)](https://github.com/CarmJos/EasyConfiguration/actions/workflows/maven.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/carmjos/easyconfiguration/badge)](https://www.codefactor.io/repository/github/carmjos/easyconfiguration)
![CodeSize](https://img.shields.io/github/languages/code-size/CarmJos/EasyConfiguration)
![](https://visitor-badge.glitch.me/badge?page_id=EasyConfiguration.readme)
# configured _(配置文件框架)_
**轻松(做)配置!**
<img src=".doc/images/logo-bg.svg" width="150px" alt="logo" align="right" style="float: right"/>
**一次配置,轻松读取!**
一款简单便捷的通用配置文件加载、读取与更新工具,可自定义配置的格式。
## 特性 & 优势
支持 [YAML](impl/yaml), [JSON](impl/json), [HOCON](impl/hocon) 和 [SQL](impl/sql) 等多种配置文件格式。
支持 [YAML](providers/yaml), [JSON](providers/gson), [HOCON](providers/hocon) 和 [SQL](providers/sql) 等多种配置文件格式。
- 基于类的配置文件初始化、加载、获取与更新机制,方便快捷。
- 支持复杂配置的手动序列化、反序列化。
@@ -32,53 +30,70 @@ README LANGUAGES [ [English](README.md) | [**中文**](README_CN.md) ]
## 开发
详细开发介绍请 [点击这里](.doc/README.md) , JavaDoc(最新Release) 请 [点击这里](https://CarmJos.github.io/EasyConfiguration) 。
详细开发介绍请 [点击这里](.doc/README.md) , JavaDoc(最新Release)
请 [点击这里](https://CarmJos.github.io/configured) 。
### 示例代码
为快速的展示该项目的适用性,这里有几个实际演示:
- [数据库配置文件实例](demo/src/main/java/cc/carm/lib/configuration/demo/DatabaseConfiguration.java)
- [全种类配置实例类演示](demo/src/main/java/cc/carm/lib/configuration/demo/tests/conf/DemoConfiguration.java)
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo) 直接查看现有的代码演示,更多复杂情况演示详见 [开发介绍](.doc/README.md) 。
您可以 [点击这里](demo/src/main/java/cc/carm/lib/configuration/demo)
直接查看现有的代码演示,更多复杂情况演示详见 [开发介绍](https://github.com/CarmJos/configured/wiki) 。
```java
@HeaderComment("Configurations for sample")
interface SampleConfig extends Configuration {
@InlineComment("Enabled?") // 行内注释
@ConfigPath(root = true)
@HeaderComments("Configurations for sample")
public interface SampleConfig extends Configuration {
@InlineComment("Enabled?") // 行后注释
ConfiguredValue<Boolean> ENABLED = ConfiguredValue.of(true);
@HeaderComments("Server configurations") // 头部注释
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class);
@HeaderComments({"[ UUID >-----------------------------------", "A lot of UUIDs"})
@FooterComments("[ UUID >-----------------------------------")
ConfiguredList<UUID> UUIDS = ConfiguredList.builderOf(UUID.class).fromString()
.parseValue(UUID::fromString).serializeValue(UUID::toString)
.parse(UUID::fromString).serialize(UUID::toString)
.defaults(
UUID.fromString("00000000-0000-0000-0000-000000000000"),
UUID.fromString("00000000-0000-0000-0000-000000000001")
).build();
interface INFO extends Configuration {
@HeaderComment("Configure your name!") // 头部注释
@HeaderComments("Configure your name!") // Header comment
ConfiguredValue<String> NAME = ConfiguredValue.of("Joker");
@ConfigPath("year") // 自定义配置路径,若不定义,则按照默认规则生成
@ConfigPath("how-old-are-you") // 自定义路径
ConfiguredValue<Integer> AGE = ConfiguredValue.of(24);
}
}
```
```java
public class Sample {
public static void main(String[] args) {
// 1. 生成一个 “Provider” 用于给配置类提供源配置的文件。
ConfigurationProvider<?> provider = EasyConfiguration.from("config.yml");
// 2. 通过 “Provider” 初始化配置类或配置实例。
provider.initialize(SampleConfig.class);
// 1. 生成一个 “holder” 用于给配置类提供源配置的文件。
ConfigurationHolder<?> holder = YAMLConfigFactory.from("target/config.yml")
.resourcePath("configs/sample.yml")
.indent(4) // Optional: Set the indentation of the configuration file.
.build();
// 2. 通过 “holder” 初始化配置类或配置实例。
holder.initialize(SampleConfig.class);
// 3. 现在可以享受快捷方便的配置文件使用方式了~
System.out.println("Enabled? -> " + SampleConfig.ENABLED.resolve());
SampleConfig.ENABLED.set(false);
System.out.println("Your name is " + SampleConfig.INFO.NAME.getNotNull() + " !");
System.out.println("And now? -> " + SampleConfig.ENABLED.resolve());
// p.s. 在本示例里的更改未保存,因此启用值在下次运行中仍将为 true。
}
}
```
@@ -86,16 +101,22 @@ public class Sample {
```yaml
# Configurations for sample
enabled: true # Enabled?
enabled: true #Enabled?
# Server configurations
port:
# [ UUID >-----------------------------------
# A lot of UUIDs
uuids:
- 00000000-0000-0000-0000-000000000000
- 00000000-0000-0000-0000-000000000001
# [ UUID >-----------------------------------
info:
# Configure your name!
name: Joker
year: 24
how-old-are-you: 24
```
### 依赖方式
@@ -116,12 +137,12 @@ info:
<name>Maven Central</name>
<url>https://repo1.maven.org/maven2</url>
</repository>
<repository>
<!--采用github依赖库,实时更新,但需要配置 (推荐) -->
<id>EasyConfiguration</id>
<id>configured</id>
<name>GitHub Packages</name>
<url>https://maven.pkg.github.com/CarmJos/EasyConfiguration</url>
<url>https://maven.pkg.github.com/CarmJos/configured</url>
</repository>
<repository>
@@ -147,7 +168,7 @@ info:
<!--基础实现部分,需要自行实现“Provider”与“Wrapper”。-->
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-core</artifactId>
<artifactId>configured-core</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
@@ -155,7 +176,7 @@ info:
<!--基于YAML文件的实现版本,可用于全部Java环境。-->
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-yaml</artifactId>
<artifactId>configured-yaml</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
@@ -164,25 +185,25 @@ info:
<!--需要注意的是,JSON不支持文件注释。-->
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-json</artifactId>
<artifactId>configured-gson</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-hocon</artifactId>
<artifactId>configured-hocon</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-sql</artifactId>
<artifactId>configured-sql</artifactId>
<version>[LATEST RELEASE]</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>
```
@@ -199,9 +220,9 @@ repositories {
// 采用Maven中心库,安全稳定,但版本更新需要等待同步
mavenCentral()
// 采用github依赖库,实时更新,但需要配置 (推荐)
maven { url 'https://maven.pkg.github.com/CarmJos/EasyConfiguration' }
maven { url 'https://maven.pkg.github.com/CarmJos/configured' }
// 采用我的私人依赖库,简单方便,但可能因为变故而无法使用
maven { url 'https://repo.carm.cc/repository/maven-public/' }
@@ -218,18 +239,18 @@ repositories {
dependencies {
//基础实现部分,需要自行实现“Provider”与“Wrapper”。
api "cc.carm.lib:easyconfiguration-core:[LATEST RELEASE]"
api "cc.carm.lib:configured-core:[LATEST RELEASE]"
//基于YAML文件的实现版本,可用于全部Java环境。
api "cc.carm.lib:easyconfiguration-yaml:[LATEST RELEASE]"
api "cc.carm.lib:configured-yaml:[LATEST RELEASE]"
//基于JSON文件的实现版本,可用于全部Java环境。
//基于JSON文件的实现版本,可用于全部Java环境。
//需要注意的是,JSON不支持文件注释。
api "cc.carm.lib:easyconfiguration-json:[LATEST RELEASE]"
api "cc.carm.lib:easyconfiguration-hocon:[LATEST RELEASE]"
api "cc.carm.lib:easyconfiguration-sql:[LATEST RELEASE]"
api "cc.carm.lib:configured-gson:[LATEST RELEASE]"
api "cc.carm.lib:configured-hocon:[LATEST RELEASE]"
api "cc.carm.lib:configured-sql:[LATEST RELEASE]"
}
```
@@ -240,20 +261,24 @@ dependencies {
### [**MineConfiguration**](https://github.com/CarmJos/MineConfiguration) (by @CarmJos )
EasyConfiguration for MineCraft!
configured for MineCraft!
开始在 MineCraft 相关服务器平台上轻松(做)配置吧!
目前支持 BungeeCord, Bukkit(Spigot) 服务端,后续将支持更多平台。
## 支持与捐赠
若您觉得本插件做的不错,您可以通过捐赠支持我!
若您觉得本插件做的不错,您可以通过[捐赠](https://github.com/sponsors/CarmJos)支持我!
感谢您对开源项目的支持!
万分感谢 Jetbrains 为我们提供了从事此项目和其他开源项目的许可!
[![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/CarmJos/EasyConfiguration)
[![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/CarmJos/configured)
万分感谢来自 [ArtformGames](https://github.com/ArtformGames) 对本项目的大力支持与积极贡献!
<img src="https://raw.githubusercontent.com/ArtformGames/.github/master/logo/logo_full.svg" width="317px" height="117px" alt="ArtformGames">
## 开源协议
+102
View File
@@ -0,0 +1,102 @@
# Security Policy
English is the authoritative language of this document.
## Supported Versions
We generally provide security fixes only for the latest released MINOR version (most recent tag on the default branch). Older versions may receive fixes only if the vulnerability is critical and a patch is low risk.
| Version | Status |
|----------|-------------------------|
| Latest | Security fixes |
| < Latest | Not routinely supported |
If you rely on an older version you are strongly encouraged to upgrade promptly after each release.
## Reporting a Vulnerability
Please DO NOT open a public Issue for suspected security problems.
Instead, email: carm@carm.cc with:
- A clear description of the issue and potential impact
- Steps to reproduce (minimal code / configuration)
- Affected version(s) and environment (JDK, OS)
- Any known workarounds
- Preferred public credit name (optional)
You will receive an acknowledgement within 72 hours (workdays) confirming receipt.
## Assessment & Disclosure Process
1. Triage & validation (attempt reproduction, scope impact)
2. Determine severity (CVSS or qualitative: Low / Moderate / High / Critical)
3. Prepare a private fix / patch + regression tests
4. Coordinate an embargoed release window (typically ≤14 days after validation for High/Critical)
5. Release a new version (and possibly backport if warranted)
6. Publish security advisory (GitHub Security Advisory + Release Notes) including mitigation steps
We may reject reports that are clearly nonsecurity bugs (e.g., feature requests, performance tuning) or issues requiring unreasonable preconditions (e.g., attacker already has full local code execution).
## Non-Qualifying Issues (Examples)
- Missing rate limits on non-authenticated, non-state-changing operations
- Denial-of-service requiring unrealistic resource constraints or already solved via JVM flags
- Vulnerabilities only exploitable on unsupported / EOL Java versions
- Social engineering, SPF/DMARC issues beyond this codebases control
## Coordinated Disclosure
If you plan to blog or speak publicly about the vulnerability prior to patch availability, please coordinate timing so users can upgrade safely.
## Dependency Security
We periodically review dependency versions for CVEs. You can help by:
- Submitting PRs that upgrade vulnerable libraries with changelog & compatibility notes
- Avoiding unnecessary new dependencies
## Cryptographic Material
This project does not bundle custom cryptographic primitives. If you discover misuse of crypto APIs or insecure random number use in security-sensitive areas, treat it as a security report.
## Reporting Format Template (Recommended)
```
Subject: [Security Report] <short title>
Affected Component: (module / class)
Version(s) Tested: x.y.z (and earlier if known)
Environment: JDK x, OS
Summary:
Describe the vulnerability and impact.
Reproduction Steps:
1. ...
2. ...
3. ...
Expected vs Actual:
Potential Impact:
Workarounds / Mitigations (if any):
Credit: (name / handle / anonymous)
```
## Credit & Acknowledgement
We will list (with permission) reporters who submit valid, first responsibly disclosed security issues in the release notes / advisory.
## GPG / Integrity
Release artifacts are signed (see project docs). Always verify signatures and checksums when consuming artifacts from Maven Central or GitHub Packages.
## Questions
For general (non-sensitive) questions, open an Issue labeled `question` rather than using the security email.
Thank you for helping keep the ecosystem safe.
+11 -4
View File
@@ -3,9 +3,9 @@
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>
<artifactId>configured-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>4.0.1</version>
<version>4.2.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>
@@ -15,9 +15,16 @@
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<artifactId>easyconfiguration-core</artifactId>
<artifactId>configured-core</artifactId>
<packaging>jar</packaging>
<name>Configured - Core</name>
<url>https://github.com/CarmJos/configured</url>
<description>
The core module of Configured framework,
providing essential configuration management functionality.
</description>
<build>
<plugins>
<plugin>
@@ -39,4 +46,4 @@
</plugins>
</build>
</project>
</project>
@@ -4,4 +4,5 @@ package cc.carm.lib.configuration;
* The root interface of the configuration file interfaces,
* which is used to label a class as a configuration.
*/
public interface Configuration { }
public interface Configuration {
}
@@ -12,9 +12,10 @@ import java.util.Objects;
* @param <TYPE> The type of the target value
*/
public class ValueAdapter<TYPE>
implements ValueSerializer<TYPE>, ValueParser<TYPE> {
implements ValueSerializer<TYPE>, ValueParser<TYPE> {
protected final @NotNull ValueType<TYPE> type;
protected @Nullable ValueSerializer<TYPE> serializer;
protected @Nullable ValueParser<TYPE> deserializer;
@@ -53,13 +54,21 @@ public class ValueAdapter<TYPE>
}
@Override
public Object serialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull TYPE value) throws Exception {
public @Nullable Object serialize(
@NotNull ConfigurationHolder<?> holder,
@NotNull ValueType<? super TYPE> type,
@NotNull TYPE value
) throws Exception {
if (serializer == null) throw new UnsupportedOperationException("Serializer is not supported");
return serializer.serialize(holder, type, value);
}
@Override
public TYPE parse(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<? super TYPE> type, @NotNull Object value) throws Exception {
public @Nullable TYPE parse(
@NotNull ConfigurationHolder<?> holder,
@NotNull ValueType<? super TYPE> type,
@NotNull Object value
) throws Exception {
if (deserializer == null) throw new UnsupportedOperationException("Deserializer is not supported");
return deserializer.parse(holder, type, value);
}
@@ -2,17 +2,19 @@ package cc.carm.lib.configuration.adapter;
import cc.carm.lib.configuration.function.DataFunction;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.Contract;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
public class ValueAdapterRegistry {
protected final Set<ValueAdapter<?>> adapters = new HashSet<>();
protected final Map<ValueType<?>, ValueAdapter<?>> adapterCache = new HashMap<>();
public <FROM, TO> void register(@NotNull Class<FROM> from, @NotNull Class<TO> to,
@Nullable DataFunction<FROM, TO> parser,
@@ -26,13 +28,14 @@ public class ValueAdapterRegistry {
ValueAdapter<FROM> fromAdapter = adapterOf(from);
if (fromAdapter == null) throw new IllegalArgumentException("No adapter for type " + from);
register(to,
serializer == null ? null : (provider, type, value) -> fromAdapter.serialize(provider, from, serializer.handle(value)),
parser == null ? null : (provider, type, data) -> parser.handle(fromAdapter.parse(provider, from, data))
serializer == null ? null : (provider, type, value) -> fromAdapter.serialize(provider, from, serializer.handle(value)),
parser == null ? null : (provider, type, data) -> parser.handle(fromAdapter.parse(provider, from, data))
);
}
public void register(@NotNull ValueAdapter<?>... adapter) {
adapters.addAll(Arrays.asList(adapter));
adapterCache.clear();
}
public <T> void register(@NotNull Class<T> type, @NotNull ValueSerializer<T> serializer) {
@@ -61,7 +64,9 @@ public class ValueAdapterRegistry {
}
}
public <T> void register(@NotNull ValueType<T> type, @Nullable ValueSerializer<T> serializer, @Nullable ValueParser<T> deserializer) {
public <T> void register(@NotNull ValueType<T> type,
@Nullable ValueSerializer<T> serializer,
@Nullable ValueParser<T> deserializer) {
if (serializer == null && deserializer == null) return;
ValueAdapter<T> existing = adapterOf(type);
if (existing != null) {
@@ -78,17 +83,31 @@ public class ValueAdapterRegistry {
public void unregister(@NotNull ValueType<?> type) {
adapters.removeIf(adapter -> adapter.type().equals(type));
adapterCache.clear();
}
@SuppressWarnings("unchecked")
public <T> @Nullable ValueAdapter<T> adapterOf(@NotNull ValueType<T> type) {
ValueAdapter<?> matched = adapters.stream().filter(adapter -> adapter.type().equals(type)).findFirst().orElse(null);
if (matched != null) return (ValueAdapter<T>) matched;
if (adapterCache.containsKey(type)) {
return (ValueAdapter<T>) adapterCache.get(type);
}
// If no adapter found, try to find the adapter for the super type
return (ValueAdapter<T>) adapters.stream()
.filter(adapter -> adapter.type().isSubtypeOf(type))
.findFirst().orElse(null);
for (ValueAdapter<?> adapter : adapters) {
if (adapter.type().equals(type)) {
adapterCache.put(type, adapter);
return (ValueAdapter<T>) adapter;
}
}
for (ValueAdapter<?> adapter : adapters) {
if (adapter.type().isSubtypeOf(type)) {
adapterCache.put(type, adapter);
return (ValueAdapter<T>) adapter;
}
}
adapterCache.put(type, null);
return null;
}
public <T> ValueAdapter<T> adapterOf(@NotNull T value) {
@@ -99,28 +118,191 @@ public class ValueAdapterRegistry {
return adapterOf(ValueType.of(type));
}
@Contract("_,_,null -> null")
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull Class<T> type, @Nullable Object source) throws Exception {
return deserialize(holder, ValueType.of(type), source);
}
@Contract("_,_,null -> null")
public <T> T deserialize(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type, @Nullable Object source) throws Exception {
if (source == null) return null; // Null check
if (type.isInstance(source)) return type.cast(source); // Not required to deserialize
if (source == null) return null;
Type typeInstance = type.getType();
if (!(typeInstance instanceof ParameterizedType) && type.isInstance(source)) {
return type.cast(source);
}
ValueAdapter<T> adapter = adapterOf(type);
if (adapter == null) throw new RuntimeException("No adapter for type " + type);
return adapter.parse(holder, type, source);
if (adapter != null) {
return adapter.parse(holder, type, source);
}
return deserializeWithoutAdapter(holder, type, source, typeInstance);
}
@Contract("_,null -> null")
private <T> T deserializeWithoutAdapter(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type,
@NotNull Object source, @NotNull Type typeInstance) throws Exception {
Class<?> rawType = type.getRawType();
if (rawType.isArray()) {
return deserializeArray(holder, type, source, rawType);
}
if (typeInstance instanceof ParameterizedType) {
return deserializeParameterized(holder, type, source, (ParameterizedType) typeInstance);
}
throw new RuntimeException("No adapter for type " + type);
}
private <T> T deserializeArray(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type,
@NotNull Object source, @NotNull Class<?> rawType) throws Exception {
List<?> list;
if (source instanceof List<?>) {
list = (List<?>) source;
} else {
// For non-list sources, treat as single element array
list = Collections.singletonList(source);
}
int size = list.size();
if (size == 0) {
return type.cast(Array.newInstance(rawType.getComponentType(), 0));
}
Class<?> componentType = rawType.getComponentType();
Object[] array = (Object[]) Array.newInstance(componentType, size);
for (int i = 0; i < size; i++) {
array[i] = deserialize(holder, componentType, list.get(i));
}
return type.cast(array);
}
@SuppressWarnings("unchecked")
private <T> T deserializeParameterized(@NotNull ConfigurationHolder<?> holder, @NotNull ValueType<T> type,
@NotNull Object source, @NotNull ParameterizedType pt) throws Exception {
Type rawType = pt.getRawType();
Type[] typeArgs = pt.getActualTypeArguments();
if (rawType == List.class || rawType == Collection.class || rawType == ArrayList.class) {
return (T) deserializeCollection(holder, source, typeArgs[0], ArrayList::new);
}
if (rawType == Set.class || rawType == HashSet.class) {
return (T) deserializeCollection(holder, source, typeArgs[0], HashSet::new);
}
if (rawType == Map.class || rawType == LinkedHashMap.class) {
return (T) deserializeMap(holder, source, typeArgs[0], typeArgs[1]);
}
throw new RuntimeException("No adapter for parameterized type " + type);
}
private Collection<?> deserializeCollection(@NotNull ConfigurationHolder<?> holder, @NotNull Object source,
@NotNull Type elementType, @NotNull java.util.function.Supplier<Collection<Object>> collectionFactory) throws Exception {
ValueType<?> elementValueType = ValueType.of(elementType);
List<?> sourceList = deserializeList(holder, elementValueType, source);
if (sourceList.isEmpty()) {
return collectionFactory.get();
}
Collection<Object> result = collectionFactory.get();
if (result instanceof ArrayList) {
((ArrayList<Object>) result).ensureCapacity(sourceList.size());
}
for (Object item : sourceList) {
Object deserializedItem = deserialize(holder, elementValueType, item);
if (deserializedItem != null) {
result.add(deserializedItem);
}
}
return result;
}
private Map<Object, Object> deserializeMap(@NotNull ConfigurationHolder<?> holder, @NotNull Object source,
@NotNull Type keyType, @NotNull Type valueType) throws Exception {
Map<?, ?> sourceMap;
if (source instanceof Map<?, ?>) {
sourceMap = (Map<?, ?>) source;
} else if (source instanceof ConfigureSection) {
sourceMap = ((ConfigureSection) source).asMap();
} else {
throw new IllegalArgumentException("Cannot deserialize to Map from " + source.getClass());
}
int mapSize = sourceMap.size();
if (mapSize == 0) {
return new LinkedHashMap<>();
}
ValueType<?> keyValueType = ValueType.of(keyType);
ValueType<?> valueValueType = ValueType.of(valueType);
Map<Object, Object> resultMap = new LinkedHashMap<>(mapSize);
for (Map.Entry<?, ?> entry : sourceMap.entrySet()) {
Object key = deserialize(holder, keyValueType, entry.getKey());
Object value = deserialize(holder, valueValueType, entry.getValue());
resultMap.put(key, value);
}
return resultMap;
}
@Nullable
public <T> Object serialize(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception {
if (value == null) return null; // Null check
ValueType<T> type = ValueType.of(value);
ValueAdapter<T> adapter = adapterOf(type);
if (adapter == null) return value; // No adapters, try to return the original value
return adapter.serialize(holder, type, value);
if (adapter != null) return adapter.serialize(holder, type, value);
if (value.getClass().isArray()) {
Object[] array = (Object[]) value;
List<Object> serializedList = new ArrayList<>(array.length);
for (Object item : array) {
serializedList.add(serialize(holder, item));
}
return serializedList;
} else if (value instanceof Collection<?>) {
Collection<?> collection = (Collection<?>) value;
List<Object> serializedList = new ArrayList<>(collection.size());
for (Object item : collection) {
serializedList.add(serialize(holder, item));
}
return serializedList;
} else if (value instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) value;
Map<Object, Object> serializedMap = new LinkedHashMap<>(map.size());
for (Map.Entry<?, ?> entry : map.entrySet()) {
Object key = serialize(holder, entry.getKey());
Object val = serialize(holder, entry.getValue());
serializedMap.put(key, val);
}
return serializedMap;
}
return value; // No adapters, and cannot handle, try to return the original value
}
protected <T> List<T> deserializeList(@NotNull ConfigurationHolder<?> holder,
@NotNull ValueType<T> type, @Nullable Object source) throws Exception {
if (source == null) return Collections.emptyList(); // Null check
if (source instanceof List<?>) {
List<?> list = (List<?>) source;
List<T> result = new ArrayList<>(list.size());
for (Object item : list) {
T deserializedItem = deserialize(holder, type, item);
if (deserializedItem != null) {
result.add(deserializedItem);
}
}
return result;
} else { // Maybe singleton? Let's try to deserialize it as a single element list
T deserializedItem = deserialize(holder, type, source);
if (deserializedItem != null) {
return Collections.singletonList(deserializedItem);
} else return Collections.emptyList();
}
}
}
@@ -2,6 +2,7 @@ package cc.carm.lib.configuration.adapter;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Value deserializer, convert base data to target value.
@@ -11,9 +12,9 @@ import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface ValueParser<TYPE> {
TYPE parse(
@NotNull ConfigurationHolder<?> holder,
@NotNull ValueType<? super TYPE> type, @NotNull Object data
@Nullable TYPE parse(
@NotNull ConfigurationHolder<?> holder,
@NotNull ValueType<? super TYPE> type, @NotNull Object data
) throws Exception;
}
@@ -2,6 +2,7 @@ package cc.carm.lib.configuration.adapter;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Value serializer, convert target value to base data.
@@ -11,9 +12,9 @@ import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface ValueSerializer<TYPE> {
Object serialize(
@NotNull ConfigurationHolder<?> holder,
@NotNull ValueType<? super TYPE> type, @NotNull TYPE value
@Nullable Object serialize(
@NotNull ConfigurationHolder<?> holder,
@NotNull ValueType<? super TYPE> type, @NotNull TYPE value
) throws Exception;
}
@@ -1,17 +1,26 @@
package cc.carm.lib.configuration.adapter;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Used to get the generic type.
* {@link ValueType} used to get the generic type of the value,
* It can be used to check if an object is an instance of a specific type,
* and to cast objects to the correct type.
* <p>
* Java's type system is not capable of retaining generic type information at runtime.
* This class is used to represent a type with its generic parameters.
* </p>
*/
public abstract class ValueType<T> {
public static final ValueType<Object> OBJECT = ofPrimitiveType(Object.class);
public static final ValueType<String> STRING = ofPrimitiveType(String.class);
public static final ValueType<Integer> INTEGER = ofPrimitiveType(Integer.class);
public static final ValueType<Integer> INTEGER_TYPE = ofPrimitiveType(int.class);
@@ -31,8 +40,8 @@ public abstract class ValueType<T> {
public static final ValueType<Character> CHAR_TYPE = ofPrimitiveType(char.class);
public static final ValueType<?>[] PRIMITIVE_TYPES = {
STRING, INTEGER, LONG, DOUBLE, FLOAT, BOOLEAN, BYTE, SHORT, CHAR,
INTEGER_TYPE, LONG_TYPE, DOUBLE_TYPE, FLOAT_TYPE, BOOLEAN_TYPE, BYTE_TYPE, SHORT_TYPE, CHAR_TYPE
STRING, INTEGER, LONG, DOUBLE, FLOAT, BOOLEAN, BYTE, SHORT, CHAR,
INTEGER_TYPE, LONG_TYPE, DOUBLE_TYPE, FLOAT_TYPE, BOOLEAN_TYPE, BYTE_TYPE, SHORT_TYPE, CHAR_TYPE
};
@SuppressWarnings("unchecked")
@@ -43,7 +52,7 @@ public abstract class ValueType<T> {
@SuppressWarnings("unchecked")
public static <T> ValueType<T> of(final Type type) {
if (type == null) throw new NullPointerException("Type cannot be null");
if (type instanceof Class<?>) { // Try handle primitive types
if (type instanceof Class<?>) { // Try to fast handle primitive types
Class<?> clazz = (Class<?>) type;
for (ValueType<?> valueType : PRIMITIVE_TYPES) {
if (valueType.getRawType() == clazz) {
@@ -55,10 +64,26 @@ public abstract class ValueType<T> {
};
}
public static <T> ValueType<T> of(final Class<T> clazz) {
public static <T> ValueType<T> of(final @NotNull Class<T> clazz) {
return of((Type) clazz);
}
public static <T> ValueType<List<T>> ofList(final @NotNull Class<T> paramType) {
return of(List.class, paramType);
}
public static <T> ValueType<List<T>> ofList(final @NotNull ValueType<T> paramType) {
return of(List.class, paramType.getType());
}
public static <K, V> ValueType<Map<K, V>> ofMap(final @NotNull Class<K> keyType, final @NotNull Class<V> valueType) {
return of(Map.class, keyType, valueType);
}
public static <K, V> ValueType<Map<K, V>> ofMap(final @NotNull ValueType<K> keyType, final @NotNull ValueType<V> valueType) {
return of(Map.class, keyType.getType(), valueType.getType());
}
/**
* Get the generic type of the complex type.
*
@@ -87,6 +112,7 @@ public abstract class ValueType<T> {
return of(parameterizedType);
}
@ApiStatus.Internal
private static <T> ValueType<T> ofPrimitiveType(Class<T> clazz) {
return new ValueType<T>(clazz) {
};
@@ -106,40 +132,65 @@ public abstract class ValueType<T> {
return type;
}
/**
* Checks if this ValueType is a subtype of the given Class.
*
* @param target The target Class to check against
* @return true if this ValueType is a subtype of the target Class, false otherwise
*/
public boolean isSubtypeOf(Class<?> target) {
Class<?> rawType = getRawType();
return target.isAssignableFrom(rawType);
}
/**
* Checks if this ValueType is a subtype of the given ValueType.
*
* @param target The target ValueType to check against
* @return true if this ValueType is a subtype of the target, false otherwise
*/
public boolean isSubtypeOf(ValueType<?> target) {
return target.isSubtypeOf(getRawType());
}
/**
* Checks if the given object is an instance of the type represented by this ValueType.
*
* @param obj The object to check
* @return true if the object is an instance of the type, false otherwise
*/
public boolean isInstance(Object obj) {
return obj != null && getRawType().isInstance(obj);
}
/**
* 提取当前 ValueType 的原始类型(Class 对象)。
* Extracts the raw type from the generic type.
*
* @return 对应的 Class 对象
* @throws IllegalStateException 如果无法提取出原始类型
* @return The raw type of the generic type
* @throws IllegalStateException if the type is not a Class or ParameterizedType
*/
public Class<?> getRawType() {
@SuppressWarnings("unchecked")
public Class<T> getRawType() {
if (type instanceof Class<?>) {
return (Class<?>) type;
return (Class<T>) type;
}
if (type instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) type;
Type raw = pt.getRawType();
if (raw instanceof Class<?>) {
return (Class<?>) raw;
return (Class<T>) raw;
}
}
throw new IllegalStateException("Unsupported type: " + type);
}
/**
* Casts the object to the type represented by this ValueType.
*
* @param obj The object to cast
* @return The object cast to the type represented by this ValueType
*/
@SuppressWarnings("unchecked")
public T cast(Object obj) {
if (!isInstance(obj)) {
@@ -148,6 +199,12 @@ public abstract class ValueType<T> {
return (T) obj;
}
/**
* Returns a string representation of the type.
* Like "{@code java.util.List<java.lang.String>}" or "java.lang.Integer".
*
* @return String representation of the type
*/
@Override
public String toString() {
if (type instanceof Class<?>) {
@@ -157,7 +214,7 @@ public abstract class ValueType<T> {
ParameterizedType pt = (ParameterizedType) type;
Type raw = pt.getRawType();
StringBuilder sb = new StringBuilder();
sb.append(raw.getTypeName());
sb.append(raw.getClass().getName());
sb.append('<');
Type[] args = pt.getActualTypeArguments();
for (int i = 0; i < args.length; i++) {
@@ -166,6 +223,7 @@ public abstract class ValueType<T> {
}
sb.append(args[i].getTypeName());
}
sb.append('>');
return sb.toString();
}
return type.getTypeName();
@@ -11,11 +11,11 @@ import java.util.Arrays;
public class PrimitiveAdapter<T> extends ValueAdapter<T> {
public static final String[] TRUE_VALUES = new String[]{
"true", "yes", "on", "1", "enabled", "enable", "active"
"true", "yes", "on", "1", "enabled", "enable", "active"
};
public static final String[] FALSE_VALUES = new String[]{
"false", "no", "off", "0", "disabled", "disable", "inactive"
"false", "no", "off", "0", "disabled", "disable", "inactive"
};
@SuppressWarnings({"unchecked", "rawtypes"})
@@ -5,26 +5,34 @@ import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
import static cc.carm.lib.configuration.adapter.strandard.PrimitiveAdapter.*;
public interface StandardAdapters {
@NotNull PrimitiveAdapter<?>[] PRIMITIVES = new PrimitiveAdapter[]{
ofString(), ofBoolean(), ofBooleanType(), ofCharacter(), ofCharacterType(),
ofInteger(), ofIntegerType(), ofLong(), ofLongType(), ofDouble(), ofDoubleType(),
ofFloat(), ofFloatType(), ofShort(), ofShortType(), ofByte(), ofByteType()
ofString(), ofBoolean(), ofBooleanType(), ofCharacter(), ofCharacterType(),
ofInteger(), ofIntegerType(), ofLong(), ofLongType(), ofDouble(), ofDoubleType(),
ofFloat(), ofFloatType(), ofShort(), ofShortType(), ofByte(), ofByteType()
};
@NotNull ValueAdapter<Enum<?>> ENUMS = PrimitiveAdapter.ofEnum();
@NotNull ValueAdapter<UUID> UUID = new ValueAdapter<>(
ValueType.of(UUID.class),
(provider, type, value) -> value.toString(),
(provider, type, value) -> java.util.UUID.fromString(value.toString())
);
@NotNull ValueAdapter<ConfigureSection> SECTIONS = new ValueAdapter<>(
ValueType.of(ConfigureSection.class),
(provider, type, value) -> value,
(provider, type, value) -> {
if (value instanceof ConfigureSection) {
return (ConfigureSection) value;
} else throw new IllegalArgumentException("Value is not a ConfigurationSection");
}
ValueType.of(ConfigureSection.class),
(provider, type, value) -> value,
(provider, type, value) -> {
if (value instanceof ConfigureSection) {
return (ConfigureSection) value;
} else throw new IllegalArgumentException("Value is not a ConfigurationSection");
}
);
}
@@ -1,6 +1,8 @@
package cc.carm.lib.configuration.builder;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.function.DataValidator;
import cc.carm.lib.configuration.function.ValueValidator;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
@@ -11,12 +13,13 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
public abstract class AbstractConfigBuilder<
TYPE, RESULT extends ConfigValue<TYPE>, HOLDER extends ConfigurationHolder<?>,
SELF extends AbstractConfigBuilder<TYPE, RESULT, HOLDER, SELF>
> {
TYPE, UNIT, RESULT extends ConfigValue<TYPE, UNIT>, HOLDER extends ConfigurationHolder<?>,
SELF extends AbstractConfigBuilder<TYPE, UNIT, RESULT, HOLDER, SELF>
> {
protected final Class<? super HOLDER> providerClass;
protected final ValueType<TYPE> type;
@@ -24,6 +27,7 @@ public abstract class AbstractConfigBuilder<
protected @Nullable HOLDER holder;
protected @Nullable String path;
protected @NotNull ValueValidator<UNIT> valueValidator = ValueValidator.none();
protected @NotNull Supplier<@Nullable TYPE> defaultValueSupplier = () -> null;
protected @NotNull BiConsumer<ConfigurationHolder<?>, String> initializer = (h, p) -> {
};
@@ -37,54 +41,121 @@ public abstract class AbstractConfigBuilder<
return type;
}
protected abstract @NotNull SELF self();
protected abstract SELF self();
public abstract @NotNull RESULT build();
public @NotNull SELF holder(@Nullable HOLDER holder) {
public SELF holder(@Nullable HOLDER holder) {
this.holder = holder;
return self();
}
public @NotNull SELF path(@Nullable String path) {
public SELF path(@Nullable String path) {
this.path = path;
return self();
}
public @NotNull SELF initializer(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
/**
* Set the {@link ValueValidator} for the value.
*
* @param validator The validator to set.
* @return this builder
*/
public SELF validator(@NotNull ValueValidator<UNIT> validator) {
this.valueValidator = validator;
return self();
}
/**
* Set the {@link DataValidator} for the value.
*
* @param validator The validator to set.
* @return this builder
*/
public SELF validator(@NotNull DataValidator<? super UNIT> validator) {
return validator((h, value) -> validator.validate(value));
}
/**
* Validate the value with the specified condition.
*
* @param validator The validator to append.
* @return this builder
*/
public SELF validate(@NotNull ValueValidator<? super UNIT> validator) {
return validator(this.valueValidator.and(validator));
}
/**
* Validate the value with the specified condition.
*
* @param validator The validator to append.
* @return this builder
*/
public SELF validate(@NotNull DataValidator<? super UNIT> validator) {
return validate((h, value) -> validator.validate(value));
}
/**
* Validate the value with the specified condition.
*
* @param condition The condition to check, if the condition is false, an exception will be thrown.
* @param exception The exception to throw if the condition is false.
* @return this builder
*/
public SELF validate(@NotNull Predicate<? super UNIT> condition, @NotNull Exception exception) {
return validate((h, value) -> {
if (!condition.test(value)) throw exception;
});
}
/**
* Validate the value with the specified condition.
*
* @param condition The condition to check, if the condition is false, an exception will be thrown.
* @param msg The message to throw if the condition is false.
* @return this builder
*/
public SELF validate(@NotNull Predicate<? super UNIT> condition, @NotNull String msg) {
return validate((h, value) -> {
if (!condition.test(value)) throw new IllegalArgumentException(msg);
});
}
public SELF initializer(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
this.initializer = initializer;
return self();
}
public @NotNull SELF append(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
public SELF append(@NotNull BiConsumer<ConfigurationHolder<?>, String> initializer) {
return initializer(initializer.andThen(initializer));
}
public @NotNull SELF append(@NotNull Consumer<ConfigurationHolder<?>> initializer) {
public SELF append(@NotNull Consumer<ConfigurationHolder<?>> initializer) {
return append((provider, valuePath) -> initializer.accept(provider));
}
public @NotNull SELF defaults(@Nullable TYPE defaultValue) {
public SELF defaults(@Nullable TYPE defaultValue) {
return defaults(() -> defaultValue);
}
public @NotNull SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) {
public SELF defaults(@NotNull Supplier<@Nullable TYPE> supplier) {
this.defaultValueSupplier = supplier;
return self();
}
public <M> @NotNull SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) {
public SELF meta(@NotNull Consumer<@NotNull ConfigurationMetaHolder> metaConsumer) {
return append((h, p) -> metaConsumer.accept(h.metadata(p)));
}
public <M> @NotNull SELF meta(@NotNull ConfigurationMetadata<M> type, @Nullable M value) {
public <M> SELF meta(@NotNull ConfigurationMetadata<M> type, @Nullable M value) {
return meta(h -> h.set(type, value));
}
protected @NotNull ValueManifest<TYPE> buildManifest() {
protected @NotNull ValueManifest<TYPE, UNIT> buildManifest() {
return new ValueManifest<>(
type(), this.defaultValueSupplier, this.initializer,
this.holder, this.path
type(), this.defaultValueSupplier, this.valueValidator,
this.initializer, this.holder, this.path
);
}
@@ -4,8 +4,11 @@ import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.value.ConfigValue;
public abstract class CommonConfigBuilder<TYPE, RESULT extends ConfigValue<TYPE>, SELF extends CommonConfigBuilder<TYPE, RESULT, SELF>>
extends AbstractConfigBuilder<TYPE, RESULT, ConfigurationHolder<?>, SELF> {
public abstract class CommonConfigBuilder<
TYPE, UNIT,
RESULT extends ConfigValue<TYPE, UNIT>,
SELF extends CommonConfigBuilder<TYPE, UNIT, RESULT, SELF>
> extends AbstractConfigBuilder<TYPE, UNIT, RESULT, ConfigurationHolder<?>, SELF> {
protected CommonConfigBuilder(ValueType<TYPE> type) {
super(ConfigurationHolder.class, type);
@@ -0,0 +1,55 @@
package cc.carm.lib.configuration.builder.collection;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import cc.carm.lib.configuration.value.impl.CollectionConfigValue;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
public abstract class SectionCollectionBuilder<
V, C extends Collection<V>,
RESULT extends CollectionConfigValue<V, C, ?>,
SELF extends SectionCollectionBuilder<V, C, RESULT, SELF>
> extends AbstractSectionBuilder<C, V, RESULT, SELF> {
protected @NotNull Supplier<? extends C> constructor;
public SectionCollectionBuilder(@NotNull Supplier<? extends C> constructor,
@NotNull ValueType<V> paramType,
@NotNull ValueHandler<ConfigureSection, V> parser,
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer) {
super(new ValueType<C>() {
}, paramType, parser, serializer);
this.constructor = constructor;
}
@SafeVarargs
public final @NotNull SELF defaults(@NotNull V... values) {
return defaults(c -> c.addAll(Arrays.asList(values)));
}
public final @NotNull SELF defaults(@NotNull Consumer<C> constructor) {
return defaults(() -> {
C collection = this.constructor.get();
constructor.accept(collection);
return collection;
});
}
public SELF constructor(@NotNull Supplier<? extends C> constructor) {
this.constructor = constructor;
return self();
}
public SELF construct(@NotNull C collection) {
return constructor(() -> collection);
}
}
@@ -0,0 +1,138 @@
package cc.carm.lib.configuration.builder.collection;
import cc.carm.lib.configuration.adapter.ValueAdapter;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import cc.carm.lib.configuration.value.ValueManifest;
import cc.carm.lib.configuration.value.impl.CollectionConfigValue;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Map;
import java.util.function.Supplier;
public class SimpleCollectionCreator<V, C extends Collection<V>, RESULT extends CollectionConfigValue<V, C, ?>> {
public static <V, C extends Collection<V>, RESULT extends CollectionConfigValue<V, C, ?>>
@NotNull SimpleCollectionCreator<V, C, RESULT> create(
@NotNull ValueType<V> type,
@NotNull Supplier<? extends C> defaultConstructor,
@NotNull CollectionValueFactory<V, C, RESULT> factory) {
return new SimpleCollectionCreator<>(type, defaultConstructor, factory);
}
protected final @NotNull Supplier<? extends C> defaultConstructor;
protected final @NotNull ValueType<V> type;
protected final @NotNull CollectionValueFactory<V, C, RESULT> factory;
public SimpleCollectionCreator(@NotNull ValueType<V> type,
@NotNull Supplier<? extends C> defaultConstructor,
@NotNull CollectionValueFactory<V, C, RESULT> factory) {
this.defaultConstructor = defaultConstructor;
this.type = type;
this.factory = factory;
}
public <S> @NotNull Source<S, V, C, RESULT> from(@NotNull Class<S> sourceType) {
return from(ValueType.of(sourceType));
}
public <S> @NotNull Source<S, V, C, RESULT> from(@NotNull ValueType<S> sourceType) {
return new Source<S, V, C, RESULT>(
defaultConstructor, sourceType, type,
ValueHandler.required(type),
ValueHandler.required(sourceType),
factory
);
}
public @NotNull SimpleCollectionCreator.Source<Object, V, C, RESULT> fromObject() {
return new Source<Object, V, C, RESULT>(
defaultConstructor, ValueType.OBJECT, type,
ValueHandler.deserialize(type), ValueHandler.toObject(),
factory
);
}
public @NotNull SimpleCollectionCreator.Source<String, V, C, RESULT> fromString() {
return new Source<String, V, C, RESULT>(
defaultConstructor, ValueType.STRING, type,
ValueHandler.required(type), ValueHandler.stringValue(),
factory
);
}
public @NotNull SimpleCollectionCreator.Section<V, C, RESULT> fromSection() {
return new Section<V, C, RESULT>(
defaultConstructor, type,
ValueHandler.required(type), ValueHandler.required(),
factory
);
}
@FunctionalInterface
public interface CollectionValueFactory<V, C, RESULT> {
@NotNull RESULT build(
@NotNull ValueManifest<C, V> manifest,
@NotNull Supplier<? extends C> constructor,
@NotNull ValueAdapter<V> paramAdapter
);
}
public static class Source<SOURCE, V, C extends Collection<V>, RESULT extends CollectionConfigValue<V, C, ?>>
extends SourceCollectionBuilder<SOURCE, V, C, RESULT, Source<SOURCE, V, C, RESULT>> {
protected final @NotNull CollectionValueFactory<V, C, RESULT> factory;
public Source(
@NotNull Supplier<? extends C> constructor,
@NotNull ValueType<SOURCE> sourceType,
@NotNull ValueType<V> paramType,
@NotNull ValueHandler<SOURCE, V> parser,
@NotNull ValueHandler<V, SOURCE> serializer,
@NotNull CollectionValueFactory<V, C, RESULT> factory) {
super(constructor, sourceType, paramType, parser, serializer);
this.factory = factory;
}
@Override
protected @NotNull SimpleCollectionCreator.Source<SOURCE, V, C, RESULT> self() {
return this;
}
@Override
public @NotNull RESULT build() {
return factory.build(buildManifest(), constructor, buildAdapter());
}
}
public static class Section<V, C extends Collection<V>, RESULT extends CollectionConfigValue<V, C, ?>>
extends SectionCollectionBuilder<V, C, RESULT, Section<V, C, RESULT>> {
protected final @NotNull CollectionValueFactory<V, C, RESULT> factory;
public Section(
@NotNull Supplier<? extends C> constructor,
@NotNull ValueType<V> paramType,
@NotNull ValueHandler<ConfigureSection, V> parser,
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer,
@NotNull CollectionValueFactory<V, C, RESULT> factory) {
super(constructor, paramType, parser, serializer);
this.factory = factory;
}
@Override
protected @NotNull SimpleCollectionCreator.Section<V, C, RESULT> self() {
return this;
}
@Override
public @NotNull RESULT build() {
return factory.build(buildManifest(), constructor, buildAdapter());
}
}
}
@@ -0,0 +1,53 @@
package cc.carm.lib.configuration.builder.collection;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.value.impl.CollectionConfigValue;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.Collection;
import java.util.function.Consumer;
import java.util.function.Supplier;
public abstract class SourceCollectionBuilder<
SOURCE, V, C extends Collection<V>,
RESULT extends CollectionConfigValue<V, C, ?>,
SELF extends SourceCollectionBuilder<SOURCE, V, C, RESULT, SELF>
>
extends AbstractSourceBuilder<C, SOURCE, V, RESULT, SELF> {
protected @NotNull Supplier<? extends C> constructor;
public SourceCollectionBuilder(@NotNull Supplier<? extends C> constructor,
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<V> paramType,
@NotNull ValueHandler<SOURCE, V> parser, @NotNull ValueHandler<V, SOURCE> serializer) {
super(new ValueType<C>() {
}, sourceType, paramType, parser, serializer);
this.constructor = constructor;
}
@SafeVarargs
public final @NotNull SELF defaults(@NotNull V... values) {
return defaults(c -> c.addAll(Arrays.asList(values)));
}
public final @NotNull SELF defaults(@NotNull Consumer<C> constructor) {
return defaults(() -> {
C collection = this.constructor.get();
constructor.accept(collection);
return collection;
});
}
public SELF constructor(@NotNull Supplier<? extends C> constructor) {
this.constructor = constructor;
return self();
}
public SELF construct(@NotNull C collection) {
return constructor(() -> collection);
}
}
@@ -4,7 +4,7 @@ import cc.carm.lib.configuration.adapter.ValueAdapter;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
import cc.carm.lib.configuration.function.DataFunction;
import cc.carm.lib.configuration.function.ValueConsumer;
import cc.carm.lib.configuration.function.ValueComposer;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import cc.carm.lib.configuration.value.ConfigValue;
@@ -14,47 +14,45 @@ import java.util.LinkedHashMap;
import java.util.Map;
public abstract class AbstractSectionBuilder<
TYPE, PARAM,
RESULT extends ConfigValue<TYPE>,
SELF extends AbstractSectionBuilder<TYPE, PARAM, RESULT, SELF>
> extends CommonConfigBuilder<TYPE, RESULT, SELF> {
TYPE, UNIT,
RESULT extends ConfigValue<TYPE, UNIT>,
SELF extends AbstractSectionBuilder<TYPE, UNIT, RESULT, SELF>
> extends CommonConfigBuilder<TYPE, UNIT, RESULT, SELF> {
protected final @NotNull ValueType<PARAM> paramType;
protected final @NotNull ValueType<UNIT> paramType;
protected @NotNull ValueHandler<ConfigureSection, PARAM> parser;
protected @NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer;
protected @NotNull ValueHandler<ConfigureSection, UNIT> parser;
protected @NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer;
protected AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<PARAM> paramType,
@NotNull ValueHandler<ConfigureSection, PARAM> parser,
@NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
protected AbstractSectionBuilder(@NotNull ValueType<TYPE> type, @NotNull ValueType<UNIT> paramType,
@NotNull ValueHandler<ConfigureSection, UNIT> parser,
@NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer) {
super(type);
this.paramType = paramType;
this.parser = parser;
this.serializer = serializer;
}
public @NotNull SELF parse(@NotNull DataFunction<ConfigureSection, PARAM> valueParser) {
public @NotNull SELF parse(@NotNull DataFunction<ConfigureSection, UNIT> valueParser) {
return parse((p, section) -> valueParser.handle(section));
}
public @NotNull SELF parse(@NotNull ValueHandler<ConfigureSection, PARAM> valueParser) {
public @NotNull SELF parse(@NotNull ValueHandler<ConfigureSection, UNIT> valueParser) {
this.parser = valueParser;
return self();
}
public @NotNull SELF serialize(@NotNull ValueHandler<PARAM, ? extends Map<String, Object>> serializer) {
public @NotNull SELF serialize(@NotNull ValueHandler<UNIT, ? extends Map<String, Object>> serializer) {
this.serializer = serializer;
return self();
}
public @NotNull SELF serialize(@NotNull DataFunction<PARAM, ? extends Map<String, Object>> serializer) {
return serialize((p, value) -> {
return serializer.handle(value);
});
public @NotNull SELF serialize(@NotNull DataFunction<UNIT, ? extends Map<String, Object>> serializer) {
return serialize((p, value) -> serializer.handle(value));
}
public @NotNull SELF serialize(@NotNull ValueConsumer<Map<String, Object>, PARAM> serializer) {
public @NotNull SELF serialize(@NotNull ValueComposer<Map<String, Object>, UNIT> serializer) {
return serialize((h, value) -> {
Map<String, Object> map = new LinkedHashMap<>();
serializer.accept(h, map, value);
@@ -62,17 +60,17 @@ public abstract class AbstractSectionBuilder<
});
}
protected ValueAdapter<PARAM> buildAdapter() {
protected ValueAdapter<UNIT> buildAdapter() {
return new ValueAdapter<>(this.paramType)
.parser((p, type, data) -> {
ConfigureSection section = p.deserialize(ConfigureSection.class, data);
if (section == null) return null;
return this.parser.handle(p, section);
})
.serializer((p, type, data) -> {
Map<String, Object> map = this.serializer.handle(p, data);
return map == null || map.isEmpty() ? null : map;
});
.parser((p, type, data) -> {
ConfigureSection section = p.deserialize(ConfigureSection.class, data);
if (section == null) return null;
return this.parser.handle(p, section);
})
.serializer((p, type, data) -> {
Map<String, Object> map = this.serializer.handle(p, data);
return map == null || map.isEmpty() ? null : map;
});
}
}
@@ -1,6 +1,8 @@
package cc.carm.lib.configuration.builder.impl;
import cc.carm.lib.configuration.adapter.ValueAdapter;
import cc.carm.lib.configuration.adapter.ValueParser;
import cc.carm.lib.configuration.adapter.ValueSerializer;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.CommonConfigBuilder;
import cc.carm.lib.configuration.function.DataFunction;
@@ -9,54 +11,63 @@ import cc.carm.lib.configuration.value.ConfigValue;
import org.jetbrains.annotations.NotNull;
public abstract class AbstractSourceBuilder<
V, SOURCE, PARAM, RESULT extends ConfigValue<V>,
SELF extends AbstractSourceBuilder<V, SOURCE, PARAM, RESULT, SELF>
> extends CommonConfigBuilder<V, RESULT, SELF> {
V, SOURCE, UNIT, RESULT extends ConfigValue<V, UNIT>,
SELF extends AbstractSourceBuilder<V, SOURCE, UNIT, RESULT, SELF>
> extends CommonConfigBuilder<V, UNIT, RESULT, SELF> {
protected final @NotNull ValueType<SOURCE> sourceType;
protected final @NotNull ValueType<PARAM> paramType;
protected @NotNull ValueHandler<SOURCE, PARAM> valueParser;
protected @NotNull ValueHandler<PARAM, SOURCE> valueSerializer;
protected final @NotNull ValueType<UNIT> paramType;
@SuppressWarnings("NotNullFieldNotInitialized") // Already initialized in constructor
protected @NotNull ValueParser<UNIT> valueParser;
@SuppressWarnings("NotNullFieldNotInitialized") // Already initialized in constructor
protected @NotNull ValueSerializer<UNIT> valueSerializer;
protected AbstractSourceBuilder(@NotNull ValueType<V> type,
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<PARAM> paramType,
@NotNull ValueHandler<SOURCE, PARAM> parser,
@NotNull ValueHandler<PARAM, SOURCE> serializer) {
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<UNIT> paramType,
@NotNull ValueHandler<SOURCE, UNIT> parser,
@NotNull ValueHandler<UNIT, SOURCE> serializer) {
super(type);
this.sourceType = sourceType;
this.paramType = paramType;
this.valueParser = parser;
this.valueSerializer = serializer;
parse(parser);
serialize(serializer);
}
public @NotNull SELF parse(@NotNull DataFunction<SOURCE, PARAM> parser) {
public @NotNull SELF parse(@NotNull DataFunction<SOURCE, UNIT> parser) {
return parse((p, source) -> parser.handle(source));
}
public @NotNull SELF parse(@NotNull ValueHandler<SOURCE, PARAM> parser) {
public @NotNull SELF parse(@NotNull ValueHandler<SOURCE, UNIT> parser) {
return parser((holder, type, data) -> {
SOURCE source = holder.deserialize(this.sourceType, data);
return parser.handle(holder, source);
});
}
public @NotNull SELF parser(@NotNull ValueParser<UNIT> parser) {
this.valueParser = parser;
return self();
}
public @NotNull SELF serialize(@NotNull ValueHandler<PARAM, SOURCE> serializer) {
public @NotNull SELF serialize(@NotNull ValueHandler<UNIT, SOURCE> serializer) {
return serializer((holder, type, data) -> {
SOURCE source = serializer.handle(holder, data);
return holder.serialize(source);
});
}
public @NotNull SELF serialize(@NotNull DataFunction<UNIT, SOURCE> serializer) {
return serialize((p, value) -> serializer.handle(value));
}
public @NotNull SELF serializer(@NotNull ValueSerializer<UNIT> serializer) {
this.valueSerializer = serializer;
return self();
}
public @NotNull SELF serialize(@NotNull DataFunction<PARAM, SOURCE> serializer) {
return serialize((p, value) -> serializer.handle(value));
}
protected ValueAdapter<PARAM> buildAdapter() {
return new ValueAdapter<>(this.paramType)
.parser((holder, type, data) -> {
SOURCE source = holder.deserialize(this.sourceType, data);
return this.valueParser.handle(holder, source);
})
.serializer((holder, type, data) -> {
SOURCE source = this.valueSerializer.handle(holder, data);
return holder.serialize(source);
});
protected ValueAdapter<UNIT> buildAdapter() {
return new ValueAdapter<>(this.paramType, this.valueSerializer, this.valueParser);
}
@@ -1,44 +0,0 @@
package cc.carm.lib.configuration.builder.list;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.function.ValueHandler;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
public class ConfigListBuilder<V> {
protected final @NotNull ValueType<V> type;
public ConfigListBuilder(@NotNull ValueType<V> type) {
this.type = type;
}
public <S> @NotNull SourceListBuilder<S, V> from(@NotNull Class<S> sourceType) {
return from(ValueType.of(sourceType));
}
public <S> @NotNull SourceListBuilder<S, V> from(@NotNull ValueType<S> sourceType) {
return new SourceListBuilder<>(
ArrayList::new, sourceType, type,
ValueHandler.required(type),
ValueHandler.required(sourceType)
);
}
public @NotNull SourceListBuilder<String, V> fromString() {
return new SourceListBuilder<>(
ArrayList::new, ValueType.STRING, type,
ValueHandler.required(type), ValueHandler.stringValue()
);
}
public @NotNull SectionListBuilder<V> fromSection() {
return new SectionListBuilder<>(
ArrayList::new, type,
ValueHandler.required(type), ValueHandler.required()
);
}
}
@@ -0,0 +1,17 @@
package cc.carm.lib.configuration.builder.list;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.collection.SimpleCollectionCreator;
import cc.carm.lib.configuration.value.standard.ConfiguredList;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class ConfigListCreator<V> extends SimpleCollectionCreator<V, List<V>, ConfiguredList<V>> {
public ConfigListCreator(@NotNull ValueType<V> type) {
super(type, ArrayList::new, ConfiguredList::new);
}
}
@@ -1,53 +0,0 @@
package cc.carm.lib.configuration.builder.list;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import cc.carm.lib.configuration.value.standard.ConfiguredList;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.Supplier;
public class SectionListBuilder<V> extends AbstractSectionBuilder<List<V>, V, ConfiguredList<V>, SectionListBuilder<V>> {
protected @NotNull Supplier<? extends List<V>> constructor;
public SectionListBuilder(@NotNull Supplier<? extends List<V>> constructor,
@NotNull ValueType<V> paramType,
@NotNull ValueHandler<ConfigureSection, V> parser,
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer) {
super(new ValueType<List<V>>() {
}, paramType, parser, serializer);
this.constructor = constructor;
}
@SafeVarargs
public final @NotNull SectionListBuilder<V> defaults(@NotNull V... values) {
return defaults(new ArrayList<>(Arrays.asList(values)));
}
public final @NotNull SectionListBuilder<V> defaults(@NotNull Collection<V> values) {
return defaults(new ArrayList<>(values));
}
public SectionListBuilder<V> constructor(@NotNull Supplier<? extends List<V>> constructor) {
this.constructor = constructor;
return this;
}
public <LIST extends List<V>> SectionListBuilder<V> construct(@NotNull LIST list) {
return constructor(() -> list);
}
@Override
protected @NotNull SectionListBuilder<V> self() {
return this;
}
@Override
public @NotNull ConfiguredList<V> build() {
return new ConfiguredList<>(buildManifest(), constructor, buildAdapter());
}
}
@@ -1,47 +0,0 @@
package cc.carm.lib.configuration.builder.list;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.impl.AbstractSourceBuilder;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.value.standard.ConfiguredList;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Supplier;
public class SourceListBuilder<SOURCE, V>
extends AbstractSourceBuilder<List<V>, SOURCE, V, ConfiguredList<V>, SourceListBuilder<SOURCE, V>> {
protected @NotNull Supplier<? extends List<V>> constructor;
public SourceListBuilder(@NotNull Supplier<? extends List<V>> constructor,
@NotNull ValueType<SOURCE> sourceType, @NotNull ValueType<V> paramType,
@NotNull ValueHandler<SOURCE, V> parser, @NotNull ValueHandler<V, SOURCE> serializer) {
super(new ValueType<List<V>>() {
}, sourceType, paramType, parser, serializer);
this.constructor = constructor;
}
@SafeVarargs
public final @NotNull SourceListBuilder<SOURCE, V> defaults(@NotNull V... values) {
return defaults(new ArrayList<>(Arrays.asList(values)));
}
public final @NotNull SourceListBuilder<SOURCE, V> defaults(@NotNull Collection<V> values) {
return defaults(new ArrayList<>(values));
}
@Override
protected @NotNull SourceListBuilder<SOURCE, V> self() {
return this;
}
@Override
public @NotNull ConfiguredList<V> build() {
return new ConfiguredList<>(buildManifest(), this.constructor, buildAdapter());
}
}
@@ -32,9 +32,9 @@ public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
public @NotNull <S> SourceMapBuilder<M, S, K, V> from(@NotNull ValueType<S> sourceType) {
return from(
sourceType,
ValueHandler.required(keyType), ValueHandler.stringValue(),
ValueHandler.required(valueType), ValueHandler.required()
sourceType,
ValueHandler.required(keyType), ValueHandler.stringValue(),
ValueHandler.required(valueType), ValueHandler.required()
);
}
@@ -44,35 +44,44 @@ public class ConfigMapBuilder<M extends Map<K, V>, K, V> {
@NotNull ValueHandler<S, V> valueParser,
@NotNull ValueHandler<V, S> valueSerializer) {
return new SourceMapBuilder<>(
this.constructor, sourceType, keyType, valueType,
keyParser, keySerializer, valueParser, valueSerializer
this.constructor, sourceType, keyType, valueType,
keyParser, keySerializer, valueParser, valueSerializer
);
}
public @NotNull <S> SourceMapBuilder<M, Object, K, V> fromObject() {
return from(
ValueType.OBJECT,
ValueHandler.deserialize(keyType), ValueHandler.stringValue(),
ValueHandler.deserialize(valueType), ValueHandler.toObject()
);
}
public @NotNull SourceMapBuilder<M, String, K, V> fromString() {
return from(
ValueType.STRING,
ValueHandler.required(keyType), ValueHandler.stringValue(),
ValueHandler.required(valueType), ValueHandler.stringValue()
ValueType.STRING,
ValueHandler.required(keyType), ValueHandler.stringValue(),
ValueHandler.required(valueType), ValueHandler.stringValue()
);
}
public @NotNull SectionMapBuilder<M, K, V> fromSection() {
return fromSection(
ValueHandler.required(keyType), ValueHandler.stringValue(),
ValueHandler.required(valueType), ValueHandler.required()
ValueHandler.required(keyType), ValueHandler.stringValue(),
ValueHandler.required(valueType), ValueHandler.required()
);
}
public @NotNull SectionMapBuilder<M, K, V> fromSection(
@NotNull ValueHandler<String, K> keyParser,
@NotNull ValueHandler<K, String> keySerializer,
@NotNull ValueHandler<ConfigureSection, V> valueParser,
@NotNull ValueHandler<V, Map<String, Object>> valueSerializer
@NotNull ValueHandler<String, K> keyParser,
@NotNull ValueHandler<K, String> keySerializer,
@NotNull ValueHandler<ConfigureSection, V> valueParser,
@NotNull ValueHandler<V, Map<String, Object>> valueSerializer
) {
return new SectionMapBuilder<>(
this.constructor, keyType, valueType,
keyParser, keySerializer, valueParser, valueSerializer
this.constructor, keyType, valueType,
keyParser, keySerializer, valueParser, valueSerializer
);
}
@@ -3,10 +3,7 @@ package cc.carm.lib.configuration.builder.map;
import cc.carm.lib.configuration.adapter.ValueType;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.*;
import java.util.function.Supplier;
public class ConfigMapCreator<K, V> {
@@ -45,4 +42,8 @@ public class ConfigMapCreator<K, V> {
return constructor(TreeMap::new);
}
public @NotNull ConfigMapBuilder<TreeMap<K, V>, K, V> asTreeMap(@NotNull Comparator<? super K> comparator) {
return constructor(() -> new TreeMap<>(comparator));
}
}
@@ -14,10 +14,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
public class SectionMapBuilder<MAP extends Map<K, V>, K, V>
extends AbstractSectionBuilder<
Map<K, V>, V, ConfiguredMap<K, V>,
SectionMapBuilder<MAP, K, V>
> {
extends AbstractSectionBuilder<Map<K, V>, V, ConfiguredMap<K, V>, SectionMapBuilder<MAP, K, V>> {
protected final @NotNull ValueType<K> keyType;
@@ -75,16 +72,20 @@ public class SectionMapBuilder<MAP extends Map<K, V>, K, V>
});
}
public @NotNull SectionMapBuilder<MAP, K, V> defaults(@NotNull K key, @NotNull V value) {
return defaults(map -> map.put(key, value));
}
public @NotNull ValueAdapter<K> buildKeyAdapter() {
return new ValueAdapter<>(this.keyType)
.parser((holder, type, data) -> {
String source = holder.deserialize(String.class, data);
return this.keyParser.handle(holder, source);
})
.serializer((holder, type, data) -> {
String source = this.keySerializer.handle(holder, data);
return holder.serialize(source);
});
.parser((holder, type, data) -> {
String source = holder.deserialize(String.class, data);
return this.keyParser.handle(holder, source);
})
.serializer((holder, type, data) -> {
String source = this.keySerializer.handle(holder, data);
return holder.serialize(source);
});
}
@Override
@@ -13,10 +13,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
public class SourceMapBuilder<MAP extends Map<K, V>, SOURCE, K, V>
extends AbstractSourceBuilder<
Map<K, V>, SOURCE, V, ConfiguredMap<K, V>,
SourceMapBuilder<MAP, SOURCE, K, V>
> {
extends AbstractSourceBuilder<Map<K, V>, SOURCE, V, ConfiguredMap<K, V>, SourceMapBuilder<MAP, SOURCE, K, V>> {
protected final @NotNull ValueType<K> keyType;
@@ -53,6 +50,10 @@ public class SourceMapBuilder<MAP extends Map<K, V>, SOURCE, K, V>
});
}
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> defaults(@NotNull K key, @NotNull V value) {
return defaults(map -> map.put(key, value));
}
public @NotNull SourceMapBuilder<MAP, SOURCE, K, V> parseKey(@NotNull DataFunction<String, K> keyParser) {
return parseKey((holder, data) -> keyParser.handle(data));
}
@@ -73,14 +74,14 @@ public class SourceMapBuilder<MAP extends Map<K, V>, SOURCE, K, V>
public @NotNull ValueAdapter<K> buildKeyAdapter() {
return new ValueAdapter<>(this.keyType)
.parser((holder, type, data) -> {
String source = holder.deserialize(String.class, data);
return this.keyParser.handle(holder, source);
})
.serializer((holder, type, data) -> {
String source = this.keySerializer.handle(holder, data);
return holder.serialize(source);
});
.parser((holder, type, data) -> {
String source = holder.deserialize(String.class, data);
return this.keyParser.handle(holder, source);
})
.serializer((holder, type, data) -> {
String source = this.keySerializer.handle(holder, data);
return holder.serialize(source);
});
}
@Override
@@ -1,7 +1,6 @@
package cc.carm.lib.configuration.builder.value;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.impl.AbstractSectionBuilder;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import org.jetbrains.annotations.NotNull;
@@ -39,8 +38,8 @@ public class ConfigValueBuilder<V> {
}
public @NotNull SectionValueBuilder<V> fromSection(
@NotNull ValueHandler<ConfigureSection, V> valueParser,
@NotNull ValueHandler<V, ? extends Map<String, Object>> valueSerializer
@NotNull ValueHandler<ConfigureSection, V> valueParser,
@NotNull ValueHandler<V, ? extends Map<String, Object>> valueSerializer
) {
return new SectionValueBuilder<>(this.type, valueParser, valueSerializer);
}
@@ -6,7 +6,8 @@ import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import org.jetbrains.annotations.NotNull;
public class SourceValueBuilder<S, V> extends AbstractSourceBuilder<V, S, V, ConfiguredValue<V>, SourceValueBuilder<S, V>> {
public class SourceValueBuilder<S, V>
extends AbstractSourceBuilder<V, S, V, ConfiguredValue<V>, SourceValueBuilder<S, V>> {
public SourceValueBuilder(@NotNull ValueType<S> sourceType, @NotNull ValueType<V> valueType,
@@ -0,0 +1,22 @@
package cc.carm.lib.configuration.function;
import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface ConfigExceptionHandler {
void handle(@NotNull String path, @NotNull Throwable throwable);
static @NotNull ConfigExceptionHandler silence() {
return (path, throwable) -> {
};
}
static @NotNull ConfigExceptionHandler print() {
return (path, throwable) -> {
System.err.println("Error occurred at path: " + path);
throwable.printStackTrace();
};
}
}
@@ -2,8 +2,10 @@ package cc.carm.lib.configuration.function;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
@FunctionalInterface
public interface DataConsumer<T> {
public interface DataConsumer<T> extends Serializable {
void accept(@NotNull T data) throws Exception;
@@ -4,10 +4,11 @@ package cc.carm.lib.configuration.function;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
import java.util.Objects;
@FunctionalInterface
public interface DataFunction<T, R> {
public interface DataFunction<T, R> extends Serializable {
@NotNull R handle(@NotNull T data) throws Exception;
@@ -0,0 +1,19 @@
package cc.carm.lib.configuration.function;
import org.jetbrains.annotations.Nullable;
import java.io.Serializable;
@FunctionalInterface
public interface DataValidator<T> extends Serializable {
void validate(@Nullable T value) throws Exception;
default DataValidator<T> compose(DataValidator<? super T> other) {
return value -> {
validate(value);
other.validate(value);
};
}
}
@@ -0,0 +1,31 @@
package cc.carm.lib.configuration.function;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
import java.io.Serializable;
@FunctionalInterface
public interface ValueComposer<T, U> extends Serializable {
/**
* Accept the value and the data, and then compose the value.
*
* @param holder The configuration holder
* @param type The value type, e.g. {@link java.util.List}, {@link java.util.Map}, etc.
* @param data The unit data
* @throws Exception If an error occurs
*/
void accept(@NotNull ConfigurationHolder<?> holder, @NotNull T type, @NotNull U data) throws Exception;
default ValueComposer<T, U> andThen(ValueComposer<? super T, ? super U> after) {
return (holder, unit, data) -> {
accept(holder, unit, data);
after.accept(holder, unit, data);
};
}
}
@@ -1,21 +0,0 @@
package cc.carm.lib.configuration.function;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
@FunctionalInterface
public interface ValueConsumer<U, T> {
void accept(@NotNull ConfigurationHolder<?> holder, @NotNull U unit, @NotNull T data) throws Exception;
default ValueConsumer<U, T> andThen(ValueConsumer<? super U, ? super T> after) {
return (holder, unit, data) -> {
accept(holder, unit, data);
after.accept(holder, unit, data);
};
}
}
@@ -7,10 +7,11 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.Serializable;
import java.util.Objects;
@FunctionalInterface
public interface ValueHandler<T, R> {
public interface ValueHandler<T, R> extends Serializable {
@Nullable R handle(@NotNull ConfigurationHolder<?> holder, @NotNull T data) throws Exception;
@@ -0,0 +1,49 @@
package cc.carm.lib.configuration.function;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.Serializable;
@FunctionalInterface
public interface ValueValidator<T> extends Serializable {
void validate(@NotNull ConfigurationHolder<?> holder, @Nullable T value) throws Exception;
default ValueValidator<T> and(ValueValidator<? super T> other) {
return (holder, value) -> {
validate(holder, value);
other.validate(holder, value);
};
}
static <V> ValueValidator<V> none() {
return (holder, data) -> {
};
}
static <V> ValueValidator<V> nonnull() {
return nonnull("Value cannot be null");
}
static <V> ValueValidator<V> nonnull(String message) {
return (holder, data) -> {
if (data == null) throw new IllegalArgumentException(message);
};
}
static <V extends Number> ValueValidator<V> range(V min, V max) {
return range(min, max, "Value must be in range [" + min + ", " + max + "]");
}
static <V extends Number> ValueValidator<V> range(V min, V max, String message) {
return (holder, data) -> {
if (data.doubleValue() < min.doubleValue() || data.doubleValue() > max.doubleValue()) {
throw new IllegalArgumentException(message);
}
};
}
}
@@ -2,7 +2,9 @@ package cc.carm.lib.configuration.source;
import cc.carm.lib.configuration.adapter.*;
import cc.carm.lib.configuration.adapter.strandard.StandardAdapters;
import cc.carm.lib.configuration.function.ConfigExceptionHandler;
import cc.carm.lib.configuration.function.DataFunction;
import cc.carm.lib.configuration.function.ValueValidator;
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
import cc.carm.lib.configuration.source.loader.PathGenerator;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
@@ -20,21 +22,30 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* ConfigurationFactory, used to create configuration holder.
*
* @param <SOURCE> The {@link ConfigureSource} type
* @param <HOLDER> The {@link ConfigurationHolder} type.
* @param <SELF> Self builder, for further implement support.
*/
public abstract class ConfigurationFactory<
SOURCE extends ConfigureSource<?, ?, SOURCE>,
HOLDER extends ConfigurationHolder<SOURCE>,
SELF
> {
SOURCE extends ConfigureSource<?, ?, SOURCE>,
HOLDER extends ConfigurationHolder<SOURCE>,
SELF
> {
protected ValueAdapterRegistry adapters = new ValueAdapterRegistry();
protected ConfigurationOptionHolder options = new ConfigurationOptionHolder();
protected @NotNull ValueAdapterRegistry adapters = new ValueAdapterRegistry();
protected @NotNull ConfigurationOptionHolder options = new ConfigurationOptionHolder();
protected @NotNull Map<String, ConfigurationMetaHolder> metadata = new HashMap<>();
protected ConfigurationInitializer initializer = new ConfigurationInitializer();
protected @NotNull ConfigurationInitializer initializer = new ConfigurationInitializer();
protected @NotNull ConfigExceptionHandler exceptionHandler = ConfigExceptionHandler.print();
public ConfigurationFactory() {
protected ConfigurationFactory() {
this.adapters.register(StandardAdapters.PRIMITIVES);
this.adapters.register(StandardAdapters.SECTIONS);
this.adapters.register(StandardAdapters.ENUMS);
this.adapters.register(StandardAdapters.UUID);
}
protected abstract SELF self();
@@ -139,18 +150,55 @@ public abstract class ConfigurationFactory<
return self();
}
public SELF pathGenerator(PathGenerator generator) {
return initializer(loader -> {
loader.pathGenerator(generator);
});
public SELF exceptionally(@NotNull ConfigExceptionHandler handler) {
this.exceptionHandler = handler;
return self();
}
/**
* Supply the base path generator for this configuration holder
*
* @param generator {@link PathGenerator}
* @return this
*/
public SELF pathGenerator(PathGenerator generator) {
return initializer(loader -> loader.pathGenerator(generator));
}
/**
* Register a new annotation for metadata to the configuration loader
*
* @param annotation The {@link Annotation}
* @param metadata The {@link ConfigurationMetadata} type
* @param extractor The {@link Function} to extract the metadata from the annotation
* @param <M> The metadata type
* @param <A> The annotation type
* @return this
*/
public <M, A extends Annotation> SELF metaAnnotation(@NotNull Class<A> annotation,
@NotNull ConfigurationMetadata<M> metadata,
@NotNull Function<A, M> extractor) {
return initializer(loader -> loader.registerAnnotation(annotation, metadata, extractor));
}
/**
* Register a new annotation for {@link ValueValidator} to the configuration loader
*
* @param annotation The {@link Annotation}
* @param builder The {@link Function} to build the {@link ValueValidator} from the annotation
* @param <A> The annotation type
* @return this
*/
public <A extends Annotation> SELF validAnnotation(@NotNull Class<A> annotation,
@NotNull Function<A, ValueValidator<Object>> builder) {
return initializer(loader -> loader.registerValidAnnotation(annotation, builder));
}
/**
* Build the configuration holder.
*
* @return The configuration holder
*/
public abstract @NotNull HOLDER build();
}
@@ -3,13 +3,16 @@ package cc.carm.lib.configuration.source;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.adapter.ValueAdapterRegistry;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.function.ConfigExceptionHandler;
import cc.carm.lib.configuration.source.loader.ConfigurationInitializer;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
import cc.carm.lib.configuration.source.meta.StandardMeta;
import cc.carm.lib.configuration.source.option.ConfigurationOption;
import cc.carm.lib.configuration.source.option.ConfigurationOptionHolder;
import cc.carm.lib.configuration.source.section.ConfigureSource;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.ValueManifest;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
@@ -17,6 +20,8 @@ import org.jetbrains.annotations.UnmodifiableView;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, SOURCE>> {
@@ -26,14 +31,25 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
protected final @NotNull ConfigurationInitializer initializer;
protected @NotNull ConfigExceptionHandler exceptionHandler;
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
@NotNull ConfigurationOptionHolder options,
@NotNull Map<String, ConfigurationMetaHolder> metadata,
@NotNull ConfigurationInitializer initializer) {
this(adapters, options, metadata, initializer, ConfigExceptionHandler.print());
}
public ConfigurationHolder(@NotNull ValueAdapterRegistry adapters,
@NotNull ConfigurationOptionHolder options,
@NotNull Map<String, ConfigurationMetaHolder> metadata,
@NotNull ConfigurationInitializer initializer,
@NotNull ConfigExceptionHandler exceptionHandler) {
this.initializer = initializer;
this.adapters = adapters;
this.options = options;
this.metadata = metadata;
this.exceptionHandler = exceptionHandler;
}
public abstract @NotNull SOURCE config();
@@ -50,6 +66,10 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
return options;
}
public <O> @NotNull O option(@NotNull ConfigurationOption<O> option) {
return options().get(option);
}
public @NotNull Map<String, ConfigurationMetaHolder> metadata() {
return this.metadata;
}
@@ -61,14 +81,26 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
@NotNull
@UnmodifiableView
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type) {
return extractMetadata(type, Objects::nonNull);
}
@NotNull
@UnmodifiableView
public <M> Map<String, M> extractMetadata(@NotNull ConfigurationMetadata<M> type, @NotNull Predicate<@Nullable M> filter) {
Map<String, M> metas = new LinkedHashMap<>();
for (Map.Entry<String, ConfigurationMetaHolder> entry : this.metadata.entrySet()) {
M data = entry.getValue().get(type);
if (data != null) metas.put(entry.getKey(), data);
if (filter.test(data)) metas.put(entry.getKey(), data);
}
return Collections.unmodifiableMap(metas);
}
@NotNull
@UnmodifiableView
public Map<String, ConfigValue<?, ?>> registeredValues() {
return extractMetadata(StandardMeta.VALUE);
}
public ValueAdapterRegistry adapters() {
return this.adapters;
}
@@ -77,17 +109,17 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
return initializer;
}
@Contract("_,null -> null")
@Nullable
public <T> T deserialize(@NotNull Class<T> type, @Nullable Object source) throws Exception {
return adapters().deserialize(this, type, source);
}
@Contract("_,null -> null")
@Nullable
public <T> T deserialize(@NotNull ValueType<T> type, @Nullable Object source) throws Exception {
return adapters().deserialize(this, type, source);
}
@Contract("null -> null")
@Nullable
public <T> Object serialize(@Nullable T value) throws Exception {
return adapters().serialize(this, value);
}
@@ -96,7 +128,7 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
try {
initializer.initialize(this, configClass);
} catch (Exception e) {
e.printStackTrace();
throwing(configClass.getName(), e);
}
}
@@ -104,12 +136,20 @@ public abstract class ConfigurationHolder<SOURCE extends ConfigureSource<?, ?, S
try {
initializer.initialize(this, config);
} catch (Exception e) {
e.printStackTrace();
throwing(config.getClass().getName(), e);
}
}
public void initialize(@NotNull ValueManifest<?> value) {
public void initialize(@NotNull ValueManifest<?, ?> value) {
value.holder(this);
}
public void throwing(@NotNull String path, @NotNull Throwable e) {
this.exceptionHandler.handle(path, e);
}
public void exceptionally(@NotNull ConfigExceptionHandler handler) {
this.exceptionHandler = handler;
}
}
@@ -5,26 +5,27 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@FunctionalInterface
public interface ConfigInitializeHandler<T> {
public interface ConfigInitializeHandler<T, V> {
static <T> ConfigInitializeHandler<T> start() {
return (provider, path, value) -> {
static <T, V> ConfigInitializeHandler<T, V> start() {
return (provider, path, value, instance) -> {
};
}
void whenInitialize(@NotNull ConfigurationHolder<?> holder, @Nullable String path, @NotNull T value) throws Exception;
void whenInitialize(@NotNull ConfigurationHolder<?> holder, @Nullable String path,
@NotNull T value, @Nullable V instance) throws Exception;
default ConfigInitializeHandler<T> andThen(ConfigInitializeHandler<T> after) {
return (provider, path, value) -> {
whenInitialize(provider, path, value);
after.whenInitialize(provider, path, value);
default ConfigInitializeHandler<T, V> andThen(ConfigInitializeHandler<T, V> after) {
return (provider, path, value, instance) -> {
whenInitialize(provider, path, value, instance);
after.whenInitialize(provider, path, value, instance);
};
}
default ConfigInitializeHandler<T> compose(ConfigInitializeHandler<T> before) {
return (provider, path, value) -> {
before.whenInitialize(provider, path, value);
whenInitialize(provider, path, value);
default ConfigInitializeHandler<T, V> compose(ConfigInitializeHandler<T, V> before) {
return (provider, path, value, instance) -> {
before.whenInitialize(provider, path, value, instance);
whenInitialize(provider, path, value, instance);
};
}
@@ -1,6 +1,7 @@
package cc.carm.lib.configuration.source.loader;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.function.ValueValidator;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetadata;
import cc.carm.lib.configuration.source.meta.StandardMeta;
@@ -21,18 +22,18 @@ import java.util.function.Function;
public class ConfigurationInitializer {
protected @NotNull PathGenerator pathGenerator;
protected @NotNull ConfigInitializeHandler<Field> fieldInitializer;
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer;
protected @NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> valueInitializer;
protected @NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer;
public ConfigurationInitializer() {
this(PathGenerator.of(), ConfigInitializeHandler.start(), ConfigInitializeHandler.start());
}
public ConfigurationInitializer(@NotNull PathGenerator pathGenerator,
@NotNull ConfigInitializeHandler<Field> fieldInitializer,
@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> valueInitializer,
@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
this.pathGenerator = pathGenerator;
this.fieldInitializer = fieldInitializer;
this.valueInitializer = valueInitializer;
this.classInitializer = classInitializer;
}
@@ -44,34 +45,34 @@ public class ConfigurationInitializer {
return pathGenerator;
}
public ConfigInitializeHandler<Field> fieldInitializer() {
return fieldInitializer;
public ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer() {
return valueInitializer;
}
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
this.fieldInitializer = fieldInitializer;
public void fieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer) {
this.valueInitializer = fieldInitializer;
}
public ConfigInitializeHandler<Class<? extends Configuration>> classInitializer() {
public ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer() {
return classInitializer;
}
public void classInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
public void classInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
this.classInitializer = classInitializer;
}
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field> fieldInitializer) {
this.fieldInitializer = this.fieldInitializer.andThen(fieldInitializer);
public void appendFieldInitializer(@NotNull ConfigInitializeHandler<Field, ConfigValue<?, ?>> fieldInitializer) {
this.valueInitializer = this.valueInitializer.andThen(fieldInitializer);
}
public void appendClassInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>> classInitializer) {
public void appendClassInitializer(@NotNull ConfigInitializeHandler<Class<? extends Configuration>, Object> classInitializer) {
this.classInitializer = this.classInitializer.andThen(classInitializer);
}
public <T, A extends Annotation> void registerClassAnnotation(@NotNull Class<A> annotation,
@NotNull ConfigurationMetadata<T> metadata,
@NotNull Function<A, T> extractor) {
appendClassInitializer((holder, path, clazz) -> {
appendClassInitializer((holder, path, clazz, instance) -> {
A data = clazz.getAnnotation(annotation);
if (data == null) return;
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
@@ -81,7 +82,7 @@ public class ConfigurationInitializer {
public <T, A extends Annotation> void registerFieldAnnotation(@NotNull Class<A> annotation,
@NotNull ConfigurationMetadata<T> metadata,
@NotNull Function<A, T> extractor) {
appendFieldInitializer((holder, path, field) -> {
appendFieldInitializer((holder, path, field, instance) -> {
A data = field.getAnnotation(annotation);
if (data == null) return;
holder.metadata(path).setIfAbsent(metadata, extractor.apply(data));
@@ -95,6 +96,14 @@ public class ConfigurationInitializer {
registerFieldAnnotation(annotation, metadata, extractor);
}
public <A extends Annotation> void registerValidAnnotation(@NotNull Class<A> annotation,
@NotNull Function<A, ValueValidator<Object>> builder) {
appendFieldInitializer((holder, path, field, instance) -> {
A data = field.getAnnotation(annotation);
if (data == null) return;
instance.validate((h, t) -> builder.apply(data).validate(h, t));
});
}
public @Nullable String getFieldPath(@NotNull ConfigurationHolder<?> holder, @Nullable String parentPath, @NotNull Field field) {
return pathGenerator.getFieldPath(holder, parentPath, field);
@@ -108,13 +117,13 @@ public class ConfigurationInitializer {
public void initialize(@NotNull ConfigurationHolder<?> holder,
@NotNull Configuration config) throws Exception {
initializeInstance(holder, config, null, null);
if (holder.options().get(StandardOptions.SET_DEFAULTS)) holder.save();
if (holder.option(StandardOptions.SET_DEFAULTS)) holder.save();
}
public void initialize(@NotNull ConfigurationHolder<?> holder,
@NotNull Class<? extends Configuration> clazz) throws Exception {
initializeStaticClass(holder, clazz, null, null);
if (holder.options().get(StandardOptions.SET_DEFAULTS)) holder.save();
if (holder.option(StandardOptions.SET_DEFAULTS)) holder.save();
}
@@ -123,9 +132,9 @@ public class ConfigurationInitializer {
@Nullable String parentPath, @Nullable Field configField) {
String path = getClassPath(holder, parentPath, root.getClass(), configField);
try {
this.classInitializer.whenInitialize(holder, path, root.getClass());
this.classInitializer.whenInitialize(holder, path, root.getClass(), root);
} catch (Exception e) {
e.printStackTrace();
holder.throwing(path, e);
}
Arrays.stream(root.getClass().getDeclaredFields()).forEach(field -> initializeField(holder, root, field, path));
}
@@ -140,16 +149,16 @@ public class ConfigurationInitializer {
String path = getClassPath(holder, parentPath, clazz, configField);
try {
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz);
this.classInitializer.whenInitialize(holder, path, (Class<? extends Configuration>) clazz, configField);
} catch (Exception e) {
e.printStackTrace();
holder.throwing(path, e);
}
for (Field field : clazz.getDeclaredFields()) {
initializeField(holder, clazz, field, path);
}
if (holder.options().get(StandardOptions.LOAD_SUB_CLASSES)) {
if (holder.option(StandardOptions.LOAD_SUB_CLASSES)) {
Class<?>[] classes = clazz.getDeclaredClasses();
for (int i = classes.length - 1; i >= 0; i--) { // 逆向加载,保持顺序。
initializeStaticClass(holder, classes[i], path, null);
@@ -163,20 +172,23 @@ public class ConfigurationInitializer {
field.setAccessible(true);
Object object = field.get(source);
//
if (object instanceof ConfigValue<?>) {
if (object instanceof ConfigValue<?, ?>) {
// 目标是 ConfigValue 实例,进行具体的初始化注入
ConfigValue<?> value = (ConfigValue<?>) object;
ConfigValue<?, ?> value = (ConfigValue<?, ?>) object;
String path = getFieldPath(holder, parent, field);
if (path == null) return;
value.initialize(holder, path);
holder.metadata(path).set(StandardMeta.UNIT, true); // Mark the minimal config value unit.
try {
this.fieldInitializer.whenInitialize(holder, path, field);
} catch (Exception e) {
e.printStackTrace();
holder.metadata(path).set(StandardMeta.VALUE, value); // Mark the minimal config value unit.
if (holder.option(StandardOptions.SET_DEFAULTS)) {
value.setDefault(); // Set default value.
}
if (holder.options().get(StandardOptions.SET_DEFAULTS)) {
value.setDefault();
try {
this.valueInitializer.whenInitialize(holder, path, field, value);
} catch (Exception e) {
holder.throwing(path, e);
}
if (holder.option(StandardOptions.PRELOAD)) {
value.get(); // Preload the value by calling #get method.
}
} else if (source instanceof Configuration && object instanceof Configuration) {
// 当且仅当 源字段与字段 均为Configuration实例时,才对目标字段进行下一步初始化加载。
@@ -9,6 +9,12 @@ import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.util.function.UnaryOperator;
/**
* Path generator for configuration.
* <p>
* Path generator is a utility class that helps to generate the path of the configuration.
* It can be used to generate the path of the field or class.
*/
public class PathGenerator {
public static PathGenerator of() {
@@ -80,7 +86,7 @@ public class PathGenerator {
}
public static char pathSeparator(ConfigurationHolder<?> holder) {
return holder.options().get(StandardOptions.PATH_SEPARATOR);
return holder.option(StandardOptions.PATH_SEPARATOR);
}
/**
@@ -94,21 +100,21 @@ public class PathGenerator {
*/
public static String covertPathName(String name) {
return name
// Replace all uppercase letters with dashes
.replaceAll("[A-Z]", "=$0")
// If the first letter is also capitalized,
// it will also be converted and the first dash will need to be removed
.replaceAll("^=(.*)$", "$1")
// Because the name may contain _, it needs to be treated a little differently
.replaceAll("_=([A-Z])", "_$1")
// The content that is not named in all caps is then converted
.replaceAll("([a-z])=([A-Z])", "$1_$2")
// Remove any extra horizontal lines
.replace("=", "")
// Replace the underscore with a dash
.replace("_", "-")
// Finally, convert it to all lowercase
.toLowerCase();
// Replace all uppercase letters with dashes
.replaceAll("[A-Z]", "=$0")
// If the first letter is also capitalized,
// it will also be converted and the first dash will need to be removed
.replaceAll("^=(.*)$", "$1")
// Because the name may contain _, it needs to be treated a little differently
.replaceAll("_=([A-Z])", "_$1")
// The content that is not named in all caps is then converted
.replaceAll("([a-z])=([A-Z])", "$1_$2")
// Remove any extra horizontal lines
.replace("=", "")
// Replace the underscore with a dash
.replace("_", "-")
// Finally, convert it to all lowercase
.toLowerCase();
}
@@ -1,10 +1,12 @@
package cc.carm.lib.configuration.source.meta;
import cc.carm.lib.configuration.value.ConfigValue;
public interface StandardMeta {
/**
* To mark the {@link cc.carm.lib.configuration.value.ConfigValue} as a minimal unit path.
* To mark the {@link ConfigValue} instance of specific path.
*/
ConfigurationMetadata<Boolean> UNIT = ConfigurationMetadata.of(false);
ConfigurationMetadata<ConfigValue<?, ?>> VALUE = ConfigurationMetadata.of();
}
@@ -6,32 +6,20 @@ import java.util.function.Supplier;
public class ConfigurationOption<V> {
@SuppressWarnings("unchecked")
public static <T> ConfigurationOption<T> of(@NotNull T defaultValue) {
return of((Class<T>) defaultValue.getClass(), defaultValue);
}
public static <T> ConfigurationOption<T> of(@NotNull Class<T> valueClazz, @NotNull T defaultValue) {
return new ConfigurationOption<>(valueClazz, defaultValue);
return new ConfigurationOption<>(defaultValue);
}
public static <T> ConfigurationOption<T> of(@NotNull Supplier<T> defaultValue) {
return of(defaultValue.get());
}
private final @NotNull Class<V> valueClazz;
private @NotNull V defaultValue;
public ConfigurationOption(@NotNull Class<V> valueClazz, @NotNull V defaultValue) {
this.valueClazz = valueClazz;
public ConfigurationOption(@NotNull V defaultValue) {
this.defaultValue = defaultValue;
}
@NotNull
public Class<V> valueClass() {
return this.valueClazz;
}
public @NotNull V defaults() {
return defaultValue;
}
@@ -23,12 +23,13 @@ public interface StandardOptions {
ConfigurationOption<Boolean> LOAD_SUB_CLASSES = of(true);
/**
* Whether to pre parse the config values.
* <br> if false, the values will be parsed when calling
* {@link cc.carm.lib.configuration.value.ConfigValue#get()}
* Whether to pre parse the config values,
* may good to set true if you want to keep the config format.
* <br> if true, the values will be parsed when
* {@link ConfigurationHolder#initialize(Configuration)}.
* <br> if false, the values will be parsed when calling
* {@link cc.carm.lib.configuration.value.ConfigValue#get()}
*/
ConfigurationOption<Boolean> PRELOAD = of(false);
ConfigurationOption<Boolean> PRELOAD = of(true);
}
@@ -1,73 +1,451 @@
package cc.carm.lib.configuration.source.section;
import cc.carm.lib.configuration.function.DataFunction;
import cc.carm.lib.configuration.source.option.StandardOptions;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnmodifiableView;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Stream;
/**
* Represents a section of a configuration.
*
* @author Carm
* @since 4.0.0
*/
public interface ConfigureSection {
@NotNull ConfigureSource<?, ?, ?> source();
/**
* Gets the parent section of this section.
* <p>
* For root sections, this will return null.
*
* @return Parent section, or null if this is a root section.
*/
@Contract(pure = true)
@Nullable ConfigureSection parent();
default char separator() {
return source().holder().options().get(StandardOptions.PATH_SEPARATOR);
/**
* Get the current section's path from {@link #parent()} of this section.
*
* @return The current path of this section, if {@link #isRoot()}, return empty string.
*/
@NotNull String path();
/**
* Get the full path of this section.
*
* @return The full path of this section, if {@link #isRoot()}, return empty string.
*/
default @NotNull String fullPath() {
if (parent() == null) return "";
return (parent().isRoot() ? "" : parent().fullPath() + pathSeparator()) + path();
}
/**
* Get the path separator for the section.
*
* @return The path separator
*/
default char pathSeparator() {
return '.';
}
/**
* Gets if this section is a root section.
*
* @return True if this section is a root section, false otherwise.
*/
@Contract(pure = true)
default boolean isRoot() {
return parent() == null;
}
/**
* Gets if this section is empty.
*
* @return True if this section is empty, false otherwise.
*/
default boolean isEmpty() {
return getValues(false).isEmpty();
}
/**
* Gets the number of keys in this section.
*
* @return Number of keys in this section.
*/
default int size(boolean deep) {
return getKeys(deep).size();
}
/**
* Gets a set containing all keys in this section.
* <p>
* If deep is set to true, then this will contain all the keys within any
* child {@link ConfigureSection}s (and their children paths).
* <p>
* If deep is set to false, then this will contain only the keys of any
* direct children, and not their own children.
*
* @param deep Whether to get a deep list.
* @return Set of keys contained within this Section.
*/
@NotNull
@UnmodifiableView
default Set<String> getKeys(boolean deep) {
return getValues(deep).keySet();
}
/**
* Gets a set containing all primary keys in this section.
*
* @return Set of keys contained within this Section.
*/
@NotNull
@UnmodifiableView
default Set<String> keys() {
return getKeys(false);
}
/**
* Gets a set containing all values in this section.
* <p>
* If deep is set to true, then this will contain all the keys within any
* child {@link ConfigureSection}s (and their children paths).
* <p>
* If deep is set to false, then this will contain only the keys of any
* direct children, and not their own children.
*
* @param deep Whether to get a deep list.
* @return Map of data values contained within this Section.
*/
@NotNull
@UnmodifiableView
Map<String, Object> getValues(boolean deep);
/**
* Gets a set containing all key-values in this section.
*
* @return Map of data values contained within this Section.
* @see #getValues(boolean)
*/
@NotNull
@UnmodifiableView
default Map<String, Object> values() {
return getValues(false);
}
/**
* Get this section as a map.
* <p>
* In this map, child {@link ConfigureSection}s will also be represented as {@link Map}s.
*
* @return Map of data values contained within this Section.
*/
@NotNull
@UnmodifiableView
Map<String, Object> asMap();
/**
* Create a stream of all values in this section.
*
* @return Stream of all values in this section.
*/
default Stream<Map.Entry<String, Object>> stream() {
return values().entrySet().stream();
}
/**
* Iterates over all key-values in this section (include child sections)
*
* @param action The action to apply to each key.
*/
default void forEach(@NotNull BiConsumer<String, Object> action) {
values().forEach(action);
}
/**
* Sets the value at the given path.
* <p>
* Null values will be kept, if you want to remove a value use {@link #remove(String)}
* Path separator depends on holder's
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
*
* @param path The path to set the value at.
* @param value The value to set.
*/
void set(@NotNull String path, @Nullable Object value);
boolean contains(@NotNull String path);
/**
* Removes the value at the given path.
* <p>
* Path separator depends on holder's
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
*
* @param path The path to remove the value at.
*/
void remove(@NotNull String path);
/**
* Check if the given path is present.
* <p>
* Path separator depends on holder's
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
*
* @param path The path to check.
* @return True if the value is present, false otherwise.
*/
default boolean contains(@NotNull String path) {
return getKeys(true).contains(path);
}
/**
* Check if the value of given path is present.
* <p>
* Path separator depends on holder's
* {@link cc.carm.lib.configuration.source.option.StandardOptions#PATH_SEPARATOR}
*
* @param path The path to check.
* @return True if the value is present, false otherwise.
*/
default boolean containsValue(@NotNull String path) {
return get(path) != null;
}
/**
* Predicate the value of given path is specific type.
*
* @param path The path to check.
* @param typeClass The type's class
* @param <T> The type to check.
* @return True if the value is present and is the correct type, false otherwise.
*/
default <T> boolean isType(@NotNull String path, @NotNull Class<T> typeClass) {
return typeClass.isInstance(get(path));
}
/**
* Predicate the value of given path is a {@link List}.
*
* @param path The path to check.
* @return True if the value is present and is a list, false otherwise.
*/
default boolean isList(@NotNull String path) {
return isType(path, List.class);
}
@Nullable List<?> getList(@NotNull String path);
/**
* Get the value as a {@link List} from the specified path.
*
* @param path The path to get the {@link List}.
* @return The list if the path exists and is a list, otherwise null.
*/
default @Nullable List<?> getList(@NotNull String path) {
Object val = get(path);
return (val instanceof List<?>) ? (List<?>) val : null;
}
/**
* Predicate the value of given path is a {@link ConfigureSection}.
*
* @param path The path to check.
* @return True if the value is present and is a section, false otherwise.
*/
default boolean isSection(@NotNull String path) {
return isType(path, ConfigureSection.class);
}
/**
* Get the value as a {@link ConfigureSection} from the specified path.
*
* @param path The path to get the section.
* @return The section if the path exists and is a section, otherwise null.
*/
@Nullable
ConfigureSection getSection(@NotNull String 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 ConfigureSection getSection(@NotNull String path) {
Object val = get(path);
return (val instanceof ConfigureSection) ? (ConfigureSection) val : null;
}
default @Nullable <T> T get(@NotNull String path, @NotNull DataFunction<Object, T> parser) {
/**
* Creates a new {@link ConfigureSection} with specified values.
* <p> The {@link #parent()} of the new section will be this section.
* <p> This section will not be saved until {@link #set(String, Object)} is called.
* <p> If you want to create and use a section and set it to this section, use {@link #computeSection(String)}.
*
* @param data The data to be used to create section.
* @return Newly created section
*/
@NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data);
/**
* Creates a new {@link ConfigureSection} with specified values.
* <p> The {@link #parent()} of the new section will be this section.
*
* @param data The data to be used to create section.
* @return Newly created section
*/
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
return createSection(path, () -> {
Map<String, Object> map = new LinkedHashMap<>();
data.accept(map);
return map;
});
}
/**
* Creates a new {@link ConfigureSection} with specified values.
* <p> The {@link #parent()} of the new section will be this section.
*
* @param data The data to be used to create section.
* @return Newly created section
*/
default @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
return createSection(path, data.get());
}
/**
* Creates a new empty {@link ConfigureSection}.
* <p> The {@link #parent()} of the new section will be this section.
*
* @return Newly created section
*/
default @NotNull ConfigureSection createSection(@NotNull String path) {
return createSection(path, new LinkedHashMap<>());
}
/**
* Get or create a section at the given path.
* <p>
* Any value previously set at this path will be replaced if it is not a section
* or will be returned if it is an exists {@link ConfigureSection}.
*
* @param path The path to get the section from.
* @return The section at the path, or a new section if it does not exist.
* @see #createSection(String, Map)
*/
default @NotNull ConfigureSection computeSection(@NotNull String path) {
return computeSection(path, new LinkedHashMap<>());
}
/**
* Get or create a section at the given path.
* <p>
* Any value previously set at this path will be replaced if it is not a section
* or will be returned if it is an exists {@link ConfigureSection}.
*
* @param path The path to get the section from.
* @return The section at the path, or a new section if it does not exist.
* @see #createSection(String, Map)
*/
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Map<?, ?> data) {
ConfigureSection current = getSection(path);
if (current == null) {
current = createSection(path, data);
set(path, current);
}
return current;
}
/**
* Get or create a section at the given path.
* <p>
* Any value previously set at this path will be replaced if it is not a section
* or will be returned if it is an exists {@link ConfigureSection}.
*
* @param path The path to get the section from.
* @return The section at the path, or a new section if it does not exist.
* @see #createSection(String, Map)
*/
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Consumer<Map<String, Object>> data) {
return computeSection(path, () -> {
Map<String, Object> map = new LinkedHashMap<>();
data.accept(map);
return map;
});
}
/**
* Get or create a section at the given path.
* <p>
* Any value previously set at this path will be replaced if it is not a section
* or will be returned if it is an exists {@link ConfigureSection}.
*
* @param path The path to get the section from.
* @return The section at the path, or a new section if it does not exist.
* @see #createSection(String, Map)
*/
default @NotNull ConfigureSection computeSection(@NotNull String path, @NotNull Supplier<Map<String, Object>> data) {
return computeSection(path, data.get());
}
/**
* Get the origin value of the path.
*
* @param path The path to get the value from.
* @return The value at the path, or null if not found.
*/
@Nullable Object get(@NotNull String path);
/**
* Get the value of the path for specific type,
* if the path does not exist, return null.
*
* @param path The path to get the value from.
* @param type The type class of the value
* @param <T> The type of the value
* @return The value at the path, or the default value if not found.
*/
default @Nullable <T> T get(@NotNull String path, @NotNull Class<T> type) {
return get(path, null, type);
}
/**
* Get the value of the path using a parser function,
* if the path does not exist, return NULL.
*
* @param path The path to get the value from.
* @param parser The function to parse the value
* @param <T> The type of the value
* @return The value at the path, or null if not found.
*/
default @Nullable <T> T get(@NotNull String path, @NotNull DataFunction<@Nullable Object, T> parser) {
return get(path, null, parser);
}
/**
* Get the value of the path for specific type,
* if the path does not exist, return the default value.
*
* @param path The path to get the value from.
* @param defaults The default value to return if the path does not exist.
* @param clazz The type class of the value
* @param <T> The type of the value
* @return The value at the path, or the default value if not found.
*/
@Contract("_,!null,_->!null")
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue, @NotNull Class<T> clazz) {
return get(path, defaultValue, DataFunction.castObject(clazz));
default @Nullable <T> T get(@NotNull String path, @Nullable T defaults, @NotNull Class<T> clazz) {
return get(path, defaults, DataFunction.castObject(clazz));
}
/**
* Get the value of the path using a parser function,
* if the path does not exist, return the default value.
*
* @param path The path to get the value from.
* @param defaultValue The default value to return if the path does not exist.
* @param parser The function to parse the value
* @param <T> The type of the value
* @return The value at the path, or the default value if not found.
*/
@Contract("_,!null,_->!null")
default @Nullable <T> T get(@NotNull String path, @Nullable T defaultValue,
@NotNull DataFunction<Object, T> parser) {
@@ -82,132 +460,297 @@ public interface ConfigureSection {
return defaultValue;
}
/**
* Predicate the value of given path is a {@link Boolean}.
*
* @param path The path to check.
* @return True if the value is present and is a boolean, false otherwise.
*/
default boolean isBoolean(@NotNull String path) {
return isType(path, Boolean.class);
}
/**
* Get the value as a {@link Boolean} from the specified path.
*
* @param path The path to get the boolean.
* @return The boolean if the path exists and is a boolean, otherwise false.
*/
default boolean getBoolean(@NotNull String path) {
return getBoolean(path, false);
return getBoolean(path, null);
}
/**
* Get the value as a {@link Boolean} from the specified path.
*
* @param path The path to get the boolean.
* @param def The default value to return if the path does not exist.
* @return The boolean if the path exists and is a boolean, otherwise the default value.
*/
@Contract("_, !null -> !null")
default @Nullable Boolean getBoolean(@NotNull String path, @Nullable Boolean def) {
return get(path, def, DataFunction.booleanValue());
}
/**
* Predicate the value of given path is a {@link Byte}.
*
* @param path The path to check.
* @return True if the value is present and is a byte, false otherwise.
*/
default @Nullable Boolean isByte(@NotNull String path) {
return isType(path, Byte.class);
}
/**
* Get the value as a {@link Byte} from the specified path.
*
* @param path The path to get the byte.
* @return The byte if the path exists and is a byte, otherwise 0.
*/
default @Nullable Byte getByte(@NotNull String path) {
return getByte(path, (byte) 0);
return getByte(path, null);
}
/**
* Get the value as a {@link Byte} from the specified path.
*
* @param path The path to get the byte.
* @param def The default value to return if the path does not exist.
* @return The byte if the path exists and is a byte, otherwise the default value.
*/
@Contract("_, !null -> !null")
default @Nullable Byte getByte(@NotNull String path, @Nullable Byte def) {
return get(path, def, DataFunction.byteValue());
}
/**
* Predicate the value of given path is a {@link Short}.
*
* @param path The path to check.
* @return True if the value is present and is a short, false otherwise.
*/
default boolean isShort(@NotNull String path) {
return isType(path, Short.class);
}
/**
* Get the value as a {@link Short} from the specified path.
*
* @param path The path to get the short.
* @return The short if the path exists and is a short, otherwise 0.
*/
default @Nullable Short getShort(@NotNull String path) {
return getShort(path, (short) 0);
return getShort(path, null);
}
/**
* Get the value as a {@link Short} from the specified path.
*
* @param path The path to get the short.
* @param def The default value to return if the path does not exist.
* @return The short if the path exists and is a short, otherwise the default value.
*/
@Contract("_, !null -> !null")
default @Nullable Short getShort(@NotNull String path, @Nullable Short def) {
return get(path, def, DataFunction.shortValue());
}
/**
* Predicate the value of given path is a {@link Integer}.
*
* @param path The path to check.
* @return True if the value is present and is an int, false otherwise.
*/
default boolean isInt(@NotNull String path) {
return isType(path, Integer.class);
}
/**
* Get the value as a {@link Integer} from the specified path.
*
* @param path The path to get the int.
* @return The int if the path exists and is an int, otherwise 0.
*/
default @Nullable Integer getInt(@NotNull String path) {
return getInt(path, 0);
return getInt(path, null);
}
/**
* Get the value as a {@link Integer} from the specified path.
*
* @param path The path to get the int.
* @param def The default value to return if the path does not exist.
* @return The int if the path exists and is an int, otherwise the default value.
*/
@Contract("_, !null -> !null")
default @Nullable Integer getInt(@NotNull String path, @Nullable Integer def) {
return get(path, def, DataFunction.intValue());
}
/**
* Predicate the value of given path is a {@link Long}.F
*
* @param path The path to check.
* @return True if the value is present and is a long, false otherwise.
*/
default boolean isLong(@NotNull String path) {
return isType(path, Long.class);
}
/**
* Get the value as a {@link Long} from the specified path.
*
* @param path The path to get the long.
* @return The long if the path exists and is a long, otherwise 0.
*/
default @Nullable Long getLong(@NotNull String path) {
return getLong(path, 0L);
return getLong(path, null);
}
/**
* Get the value as a {@link Long} from the specified path.
*
* @param path The path to get the long.
* @param def The default value to return if the path does not exist.
* @return The long if the path exists and is a long, otherwise the default value.
*/
@Contract("_, !null -> !null")
default @Nullable Long getLong(@NotNull String path, @Nullable Long def) {
return get(path, def, DataFunction.longValue());
}
/**
* Predicate the value of given path is a {@link Float}.
*
* @param path The path to check.
* @return True if the value is present and is a float, false otherwise.
*/
default boolean isFloat(@NotNull String path) {
return isType(path, Float.class);
}
/**
* Get the value as a {@link Float} from the specified path.
*
* @param path The path to get the float.
* @return The float if the path exists and is a float, otherwise 0.
*/
default @Nullable Float getFloat(@NotNull String path) {
return getFloat(path, 0.0F);
return getFloat(path, null);
}
/**
* Get the value as a {@link Float} from the specified path.
*
* @param path The path to get the float.
* @param def The default value to return if the path does not exist.
* @return The float if the path exists and is a float, otherwise the default value.
*/
@Contract("_, !null -> !null")
default @Nullable Float getFloat(@NotNull String path, @Nullable Float def) {
return get(path, def, DataFunction.floatValue());
}
/**
* Predicate the value of given path is a {@link Double}.
*
* @param path The path to check.
* @return True if the value is present and is a double, false otherwise.
*/
default boolean isDouble(@NotNull String path) {
return isType(path, Double.class);
}
/**
* Get the value as a {@link Double} from the specified path.
*
* @param path The path to get the double.
* @return The double if the path exists and is a double, otherwise 0.
*/
default @Nullable Double getDouble(@NotNull String path) {
return getDouble(path, 0.0D);
return getDouble(path, null);
}
/**
* Get the value as a {@link Double} from the specified path.
*
* @param path The path to get the double.
* @param def The default value to return if the path does not exist.
* @return The double if the path exists and is a double, otherwise the default value.
*/
@Contract("_, !null -> !null")
default @Nullable Double getDouble(@NotNull String path, @Nullable Double def) {
return get(path, def, DataFunction.doubleValue());
}
/**
* Predicate the value of given path is a {@link Character}.
*
* @param path The path to check.
* @return True if the value is present and is a char, false otherwise.
*/
default boolean isChar(@NotNull String path) {
return isType(path, Boolean.class);
return isType(path, Character.class);
}
/**
* Get the value as a {@link Character} from the specified path.
*
* @param path The path to get the char.
* @return The char if the path exists and is a char, otherwise null.
*/
default @Nullable Character getChar(@NotNull String path) {
return getChar(path, null);
}
/**
* Get the value as a {@link Character} from the specified path.
*
* @param path The path to get the char.
* @param def The default value to return if the path does not exist.
* @return The char if the path exists and is a char, otherwise the default value.
*/
@Contract("_, !null -> !null")
default @Nullable Character getChar(@NotNull String path, @Nullable Character def) {
return get(path, def, Character.class);
}
/**
* Predicate the value of given path is a {@link String}.
*
* @param path The path to check.
* @return True if the value is present and is a string, false otherwise.
*/
default boolean isString(@NotNull String path) {
return isType(path, String.class);
}
/**
* Get the value as a {@link String} from the specified path.
*
* @param path The path to get the string.
* @return The string if the path exists and is a string, otherwise null.
*/
default @Nullable String getString(@NotNull String path) {
return getString(path, null);
}
/**
* Get the value as a {@link String} from the specified path.
*
* @param path The path to get the string.
* @param def The default value to return if the path does not exist.
* @return The string if the path exists and is a string, otherwise the default value.
*/
@Contract("_, !null -> !null")
default @Nullable String getString(@NotNull String path, @Nullable String def) {
return get(path, def, String.class);
}
/**
* Get a list of values from the section
* Get a list of values from current section
* <p>
* If the path does not exist, an empty list will be returned
* <br>Any changes please use {@link #set(String, Object)} after changes
@@ -222,7 +765,7 @@ public interface ConfigureSection {
}
/**
* Get a list of strings from the section
* Get a list of strings from current section
* <p> Limitations see {@link #getList(String, DataFunction)}
*
* @param path The path to get the list from
@@ -233,7 +776,7 @@ public interface ConfigureSection {
}
/**
* Get a list of integer from the section
* Get a list of integer from current section
* <p> Limitations see {@link #getList(String, DataFunction)}
*
* @param path The path to get the list from
@@ -244,7 +787,7 @@ public interface ConfigureSection {
}
/**
* Get a list of long from the section
* Get a list of long from current section
* <p> Limitations see {@link #getList(String, DataFunction)}
*
* @param path The path to get the list from
@@ -255,7 +798,7 @@ public interface ConfigureSection {
}
/**
* Get a list of double from the section
* Get a list of double from current section
* <p> Limitations see {@link #getList(String, DataFunction)}
*
* @param path The path to get the list from
@@ -266,7 +809,7 @@ public interface ConfigureSection {
}
/**
* Get a list of floats from the section
* Get a list of floats from current section
* <p> Limitations see {@link #getList(String, DataFunction)}
*
* @param path The path to get the list from
@@ -277,7 +820,7 @@ public interface ConfigureSection {
}
/**
* Get a list of bytes from the section
* Get a list of bytes from current section
* <p> Limitations see {@link #getList(String, DataFunction)}
*
* @param path The path to get the list from
@@ -288,7 +831,7 @@ public interface ConfigureSection {
}
/**
* Get a list of char from the section
* Get a list of char from current section
* <p> Limitations see {@link #getList(String, DataFunction)}
*
* @param path The path to get the list from
@@ -298,24 +841,71 @@ public interface ConfigureSection {
return getList(path, DataFunction.castObject(Character.class));
}
/**
* Get a list of {@link ConfigureSection} from current section
*
* @param path The path to get the list from
* @return The list of {@link ConfigureSection}
*/
default @NotNull List<ConfigureSection> getSectionList(@NotNull String path) {
return getList(path, obj -> {
if (obj instanceof ConfigureSection) {
return (ConfigureSection) obj;
} else if (obj instanceof Map) {
return createSection(childPath(path), (Map<?, ?>) obj);
}
return null;
});
}
/**
* Get the specific type of collection from current section.
*
* @param path The path to get the collection from
* @param constructor The constructor of the collection
* @param parser The function to parse the values
* @param <T> The type of the values
* @param <C> The type of the collection
* @return The collection of values
*/
default <T, C extends Collection<T>> @NotNull C getCollection(@NotNull String path,
@NotNull Supplier<C> constructor,
@NotNull DataFunction<Object, T> parser) {
return parseCollection(getList(path), constructor, parser);
}
/**
* Get the specific type of steam from current section.
*
* @param path The path to get the stream from
* @return The stream of values
*/
default @NotNull Stream<?> stream(@NotNull String path) {
List<?> values = getList(path);
return values == null ? Stream.empty() : values.stream();
}
/**
* Get the specific type of steam from current section.
*
* @param path The path to get the stream from
* @param parser The function to parse the values
* @param <T> The type of the values
* @return The stream of values
*/
default <T> @NotNull Stream<T> stream(@NotNull String path, @NotNull Function<Object, T> parser) {
return stream(path).map(parser);
}
default String childPath(String path) {
int index = path.indexOf(pathSeparator());
return (index == -1) ? path : path.substring(index + 1);
}
static <T, C extends Collection<T>> @NotNull C parseCollection(
@Nullable List<?> data, @NotNull Supplier<C> constructor,
@NotNull DataFunction<Object, T> parser
@Nullable List<?> data,
@NotNull Supplier<C> constructor,
@NotNull DataFunction<Object, T> parser
) {
C values = constructor.get();
if (data == null) return values;
@@ -1,16 +1,25 @@
package cc.carm.lib.configuration.source.section;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import cc.carm.lib.configuration.source.option.StandardOptions;
import org.jetbrains.annotations.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* ConfigureSource represents the source of configuration,
* which can be a file, a database, or any other source.
*
* @param <SECTION> The type of the root section.
* @param <ORIGINAL> The original configuration object.
* @param <SELF> The type of the source itself, for further implement support.
* @see ConfigureSection
*/
public abstract class ConfigureSource<
SECTION extends ConfigureSection, ORIGINAL,
SELF extends ConfigureSource<SECTION, ORIGINAL, SELF>>
implements ConfigureSection {
SECTION extends ConfigureSection, ORIGINAL,
SELF extends ConfigureSource<SECTION, ORIGINAL, SELF>>
implements ConfigureSection {
protected final @NotNull ConfigurationHolder<? extends SELF> holder;
protected long lastUpdateMillis;
@@ -24,28 +33,46 @@ public abstract class ConfigureSource<
return holder;
}
public void reload() throws Exception {
onReload(); // 调用重写的Reload方法
this.lastUpdateMillis = System.currentTimeMillis();
}
protected abstract SELF self();
@Contract(pure = true)
@ApiStatus.Internal
protected abstract @NotNull SELF self();
/**
* @return Original configuration object
* @return The original configuration object.
*/
@Contract(pure = true)
public abstract @NotNull ORIGINAL original();
/**
* @return The root {@link ConfigureSection}
* @return The root {@link ConfigureSection}, which represents the entire configuration.
*/
public abstract @NotNull SECTION section();
/**
* Save the whole configuration.
*
* @throws Exception If any error occurs while saving.
*/
public abstract void save() throws Exception;
/**
* Reload the configuration.
* <br>This used for implementation, for external usage, use {@link #reload()}
*
* @throws Exception If any error occurs while reloading.
*/
@ApiStatus.OverrideOnly
protected abstract void onReload() throws Exception;
public char pathSeparator() {
return holder().options().get(StandardOptions.PATH_SEPARATOR);
}
public long getLastUpdateMillis() {
return this.lastUpdateMillis;
}
@@ -54,14 +81,20 @@ public abstract class ConfigureSource<
return getLastUpdateMillis() > parsedTime;
}
/**
* Source also represents the root section, so it has no parent
*
* @return null
*/
@Override
@Contract(pure = true, value = "->null")
public @Nullable ConfigureSection parent() {
return null; // Source also represents the root section, so it has no parent
return null;
}
@Override
public @NotNull ConfigureSource<?, ?, ?> source() {
return self();
public @NotNull String path() {
return "";
}
@Override
@@ -69,34 +102,29 @@ public abstract class ConfigureSource<
return section().getValues(deep);
}
@Override
public @NotNull @UnmodifiableView Set<String> getKeys(boolean deep) {
return section().getKeys(deep);
}
@Override
public @NotNull @UnmodifiableView Map<String, Object> asMap() {
return section().asMap();
}
@Override
public @NotNull ConfigureSection createSection(@NotNull String path, @NotNull Map<?, ?> data) {
return section().createSection(path, data);
}
@Override
public void set(@NotNull String path, @Nullable Object value) {
section().set(path, value);
}
@Override
public boolean contains(@NotNull String path) {
return section().contains(path);
}
@Override
public boolean isList(@NotNull String path) {
return section().isList(path);
}
@Override
public @Nullable List<?> getList(@NotNull String path) {
return section().getList(path);
}
@Override
public boolean isSection(@NotNull String path) {
return section().isSection(path);
}
@Override
public @Nullable ConfigureSection getSection(@NotNull String path) {
return section().getSection(path);
public void remove(@NotNull String path) {
section().remove(path);
}
@Override
@@ -1,6 +1,7 @@
package cc.carm.lib.configuration.value;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,9 +34,9 @@ import java.util.Optional;
* @see ValueManifest Base class providing metadata and default value handling
* @see ConfigurationHolder Responsible for configuration source persistence
*/
public abstract class ConfigValue<T> extends ValueManifest<T> {
public abstract class ConfigValue<T, U> extends ValueManifest<T, U> {
protected ConfigValue(@NotNull ValueManifest<T> manifest) {
protected ConfigValue(@NotNull ValueManifest<T, U> manifest) {
super(manifest);
}
@@ -54,7 +55,18 @@ public abstract class ConfigValue<T> extends ValueManifest<T> {
* @return Configured value or default value
*/
public T getOrDefault() {
return optional().orElse(defaults());
return getOr(defaults());
}
/**
* Gets the configured value, or returns the specified default value if not present.
*
* @param defaults The default value to return if the configured value is not present
* @return Configured value or specified default value
*/
@Contract("!null -> !null")
public T getOr(T defaults) {
return optional().orElse(defaults);
}
/**
@@ -113,7 +125,7 @@ public abstract class ConfigValue<T> extends ValueManifest<T> {
* @param override Whether to overwrite existing configured value
*/
public void setDefault(boolean override) {
if (!override && config().contains(path())) return;
if (config().contains(path()) && !override) return; // Skip if the value is already set
set(defaults()); // Set the default value
}
@@ -138,4 +150,4 @@ public abstract class ConfigValue<T> extends ValueManifest<T> {
holder().save();
}
}
}
@@ -1,47 +1,59 @@
package cc.carm.lib.configuration.value;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.function.ValueValidator;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.meta.ConfigurationMetaHolder;
import cc.carm.lib.configuration.source.section.ConfigureSource;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
public class ValueManifest<T> {
public class ValueManifest<TYPE, UNIT> {
protected final @NotNull ValueType<T> type;
protected final @NotNull ValueType<TYPE> type;
protected final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer;
protected @Nullable ConfigurationHolder<?> holder;
protected @Nullable String path; // Section path
protected @NotNull Supplier<@Nullable T> defaultSupplier;
protected @NotNull ValueValidator<UNIT> validator;
protected @NotNull Supplier<@Nullable TYPE> defaultSupplier;
public ValueManifest(@NotNull ValueType<T> type) {
this(type, () -> null, EMPTY_INITIALIZER, null, null);
public ValueManifest(@NotNull ValueType<TYPE> type) {
this(type, () -> null, ValueValidator.none(), EMPTY_INITIALIZER, null, null);
}
public ValueManifest(@NotNull T defaultValue) {
public ValueManifest(@NotNull TYPE defaultValue) {
this(ValueType.of(defaultValue), () -> defaultValue);
}
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier) {
this(type, defaultSupplier, EMPTY_INITIALIZER, null, null);
public ValueManifest(@NotNull ValueType<TYPE> type, @NotNull Supplier<@Nullable TYPE> defaultSupplier) {
this(type, defaultSupplier, ValueValidator.none(), EMPTY_INITIALIZER, null, null);
}
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier,
public ValueManifest(@NotNull ValueType<TYPE> type,
@NotNull Supplier<@Nullable TYPE> defaultSupplier,
@NotNull ValueValidator<UNIT> validator) {
this(type, defaultSupplier, validator, EMPTY_INITIALIZER, null, null);
}
public ValueManifest(@NotNull ValueType<TYPE> type, @NotNull Supplier<@Nullable TYPE> defaultSupplier,
@NotNull ValueValidator<UNIT> validator,
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer) {
this(type, defaultSupplier, initializer, null, null);
this(type, defaultSupplier, validator, initializer, null, null);
}
public ValueManifest(@NotNull ValueType<T> type, @NotNull Supplier<@Nullable T> defaultSupplier,
public ValueManifest(@NotNull ValueType<TYPE> type,
@NotNull Supplier<@Nullable TYPE> defaultSupplier,
@NotNull ValueValidator<UNIT> validator,
@NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> initializer,
@Nullable ConfigurationHolder<?> holder, @Nullable String path) {
this.type = type;
this.validator = validator;
this.initializer = initializer;
this.defaultSupplier = defaultSupplier;
this.holder = holder;
@@ -49,8 +61,8 @@ public class ValueManifest<T> {
initialize();
}
protected ValueManifest(@NotNull ValueManifest<T> manifest) {
this(manifest.type, manifest.defaultSupplier, manifest.initializer, manifest.holder, manifest.path);
protected ValueManifest(@NotNull ValueManifest<TYPE, UNIT> manifest) {
this(manifest.type, manifest.defaultSupplier, manifest.validator, manifest.initializer, manifest.holder, manifest.path);
}
public void initialize(@NotNull ConfigurationHolder<?> holder, @NotNull String path) {
@@ -63,7 +75,7 @@ public class ValueManifest<T> {
if (holder != null && path != null) this.initializer.accept(holder, path);
}
public @NotNull ValueType<T> type() {
public @NotNull ValueType<TYPE> type() {
return this.type;
}
@@ -75,50 +87,77 @@ public class ValueManifest<T> {
this.path = path;
}
public @Nullable T defaults() {
public @Nullable TYPE defaults() {
return this.defaultSupplier.get();
}
public void defaults(@Nullable T defaultValue) {
public void defaults(@Nullable TYPE defaultValue) {
defaults(() -> defaultValue);
}
public void defaults(@NotNull Supplier<@Nullable T> defaultValue) {
public void defaults(@NotNull Supplier<@Nullable TYPE> defaultValue) {
this.defaultSupplier = defaultValue;
}
public boolean hasDefaults() {
return this.defaultSupplier.get() != null;
return defaults() != null;
}
public @NotNull ValueValidator<UNIT> validator() {
return this.validator;
}
public void validator(@NotNull ValueValidator<UNIT> validator) {
this.validator = validator;
}
public void validate(@NotNull ValueValidator<UNIT> validator) {
validator(this.validator.and(validator));
}
protected UNIT withValidated(@Nullable UNIT value) throws Exception {
validator.validate(holder(), value);
return value;
}
public @NotNull String path() {
if (path != null) return path;
else throw new IllegalStateException("No section path provided.");
else throw new IllegalStateException("No section path provided for Value(" + type() + ").");
}
public @NotNull ConfigurationHolder<?> holder() {
if (this.holder != null) return this.holder;
throw new IllegalStateException("Value does not have a provider.");
throw new IllegalStateException("Value(" + type() + ") does not have a provider.");
}
public @NotNull ConfigureSource<?, ?, ?> config() {
return holder().config();
}
public ConfigurationMetaHolder metadata() {
public @NotNull ConfigurationMetaHolder metadata() {
return holder().metadata(path());
}
protected Object getData() {
@ApiStatus.Internal
protected @Nullable Object getData() {
return config().get(path());
}
@ApiStatus.Internal
protected void setData(@Nullable Object value) {
config().set(path(), value);
}
protected void throwing(@NotNull Throwable throwable) {
throwing(path, throwable);
}
private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, path) -> {
protected void throwing(@NotNull String path, @NotNull Throwable throwable) {
if (holder == null) throwable.printStackTrace();
else holder.throwing(path, throwable);
}
private static final @NotNull BiConsumer<@NotNull ConfigurationHolder<?>, @NotNull String> EMPTY_INITIALIZER = (provider, valuePath) -> {
};
}
@@ -9,12 +9,12 @@ import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class CachedConfigValue<T> extends ConfigValue<T> {
public abstract class CachedConfigValue<T, U> extends ConfigValue<T, U> {
protected @Nullable T cachedValue;
protected long parsedTime = -1;
protected CachedConfigValue(@NotNull ValueManifest<T> manifest) {
protected CachedConfigValue(@NotNull ValueManifest<T, U> manifest) {
super(manifest);
}
@@ -0,0 +1,210 @@
package cc.carm.lib.configuration.value.impl;
import cc.carm.lib.configuration.adapter.ValueAdapter;
import cc.carm.lib.configuration.adapter.ValueParser;
import cc.carm.lib.configuration.adapter.ValueSerializer;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.value.ValueManifest;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* Base implementation of a collection config value, like {@link List} or {@link Set}.
*
* @param <V> Value type
* @param <C> Collection type
* @param <SELF> Self reference type (used for internal call or recursive generics)
*/
public abstract class CollectionConfigValue<
V, C extends Collection<V>,
SELF extends CollectionConfigValue<V, C, SELF>
> extends CachedConfigValue<C, V> implements Collection<V> {
protected final @NotNull Supplier<? extends C> constructor;
protected final @NotNull ValueAdapter<V> paramAdapter;
public CollectionConfigValue(@NotNull ValueManifest<C, V> manifest,
@NotNull Supplier<? extends C> constructor,
@NotNull ValueAdapter<V> paramAdapter) {
super(manifest);
this.constructor = constructor;
this.paramAdapter = paramAdapter;
}
/**
* @return Adapter of this value.
*/
public @NotNull ValueAdapter<V> adapter() {
return this.paramAdapter;
}
public @NotNull ValueType<V> paramType() {
return adapter().type();
}
/**
* @return Value's parser, parse base object to value.
*/
public @Nullable ValueParser<V> parser() {
return parserFor(adapter());
}
/**
* @return Value's serializer, parse value to base object.
*/
public @Nullable ValueSerializer<V> serializer() {
return serializerFor(adapter());
}
private @NotNull C createCollection() {
return constructor.get();
}
@Override
public @NotNull C get() {
if (!cacheExpired()) return getCachedOrDefault(createCollection());
// Data that is outdated and needs to be parsed again.
C set = createCollection();
try {
List<?> data = config().contains(path()) ? config().getList(path()) : null;
if (data == null) return getDefaultFirst(set);
ValueParser<V> parser = parser();
if (parser == null) return getDefaultFirst(set);
int i = 0;
for (Object dataVal : data) {
if (dataVal == null) continue;
try {
set.add(withValidated(parser.parse(holder(), paramType(), dataVal)));
} catch (Exception e) {
throwing(path + "[" + i + "]", e);
}
}
} catch (Exception ex) {
throwing(ex);
}
return updateCache(set);
}
@Override
public void set(@Nullable C collection) {
updateCache(collection);
if (collection == null) {
setData(null);
return;
}
ValueSerializer<V> serializer = serializer();
if (serializer == null) return;
List<Object> data = new ArrayList<>();
for (V val : collection) {
if (val == null) continue;
try {
data.add(serializer.serialize(holder(), paramType(), withValidated(val)));
} catch (Exception ex) {
throwing(ex);
}
}
setData(data);
}
public @NotNull C copy() {
C other = createCollection();
other.addAll(resolve());
return other;
}
public abstract @NotNull SELF self();
public <T> @NotNull T handle(Function<C, T> function) {
C list = resolve();
T result = function.apply(list);
set(list);
return result;
}
public @NotNull SELF modify(Consumer<C> consumer) {
C list = resolve();
consumer.accept(list);
set(list);
return self();
}
@Override
public int size() {
return resolve().size();
}
@Override
public boolean isEmpty() {
return resolve().isEmpty();
}
@Override
public boolean contains(Object o) {
return resolve().contains(o);
}
@NotNull
@Override
public Iterator<V> iterator() {
return resolve().iterator();
}
@NotNull
@Override
public Object @NotNull [] toArray() {
return resolve().toArray();
}
@NotNull
@Override
public <T> T @NotNull [] toArray(@NotNull T[] a) {
return resolve().toArray(a);
}
@Override
public boolean containsAll(@NotNull Collection<?> c) {
return new HashSet<>(resolve()).containsAll(c);
}
@Override
public boolean add(V v) {
handle(list -> list.add(v));
return true;
}
@Override
public boolean addAll(@NotNull Collection<? extends V> c) {
return handle(list -> list.addAll(c));
}
@Override
public boolean remove(Object o) {
return handle(list -> list.remove(o));
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
return handle(list -> list.removeAll(c));
}
@Override
public boolean retainAll(@NotNull Collection<?> c) {
return handle(list -> list.retainAll(c));
}
@Override
public void clear() {
modify(Collection::clear);
}
}
@@ -1,112 +1,47 @@
package cc.carm.lib.configuration.value.standard;
import cc.carm.lib.configuration.adapter.ValueAdapter;
import cc.carm.lib.configuration.adapter.ValueParser;
import cc.carm.lib.configuration.adapter.ValueSerializer;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.list.ConfigListBuilder;
import cc.carm.lib.configuration.builder.collection.SimpleCollectionCreator;
import cc.carm.lib.configuration.builder.list.ConfigListCreator;
import cc.carm.lib.configuration.value.ValueManifest;
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
import cc.carm.lib.configuration.value.impl.CollectionConfigValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements List<V> {
public class ConfiguredList<V> extends CollectionConfigValue<V, List<V>, ConfiguredList<V>> implements List<V> {
public static <T> @NotNull ConfigListBuilder<T> builderOf(@NotNull Class<T> type) {
public static <T> @NotNull ConfigListCreator<T> builderOf(@NotNull Class<T> type) {
return builderOf(ValueType.of(type));
}
public static <T> @NotNull ConfigListBuilder<T> builderOf(@NotNull ValueType<T> type) {
return new ConfigListBuilder<>(type);
public static <T> @NotNull ConfigListCreator<T> builderOf(@NotNull ValueType<T> type) {
return new ConfigListCreator<>(type);
}
protected final @NotNull Supplier<? extends List<V>> constructor;
protected final @NotNull ValueAdapter<V> paramAdapter;
public static <T>
@NotNull SimpleCollectionCreator.Source<Object, T, List<T>, ConfiguredList<T>> with(@NotNull Class<T> registeredType) {
return with(ValueType.of(registeredType));
}
public ConfiguredList(@NotNull ValueManifest<List<V>> manifest,
public static <T> @NotNull SimpleCollectionCreator.Source<Object, T, List<T>, ConfiguredList<T>> with(@NotNull ValueType<T> registeredType) {
return builderOf(registeredType).fromObject();
}
@SafeVarargs
public static <T> @NotNull ConfiguredList<T> of(@NotNull T value, @NotNull T... values) {
List<T> list = new ArrayList<>();
list.add(value);
Collections.addAll(list, values);
return with(ValueType.of(value)).defaults(list).build();
}
public ConfiguredList(@NotNull ValueManifest<List<V>, V> manifest,
@NotNull Supplier<? extends List<V>> constructor,
@NotNull ValueAdapter<V> paramAdapter) {
super(manifest);
this.constructor = constructor;
this.paramAdapter = paramAdapter;
}
/**
* @return Adapter of this value.
*/
public @NotNull ValueAdapter<V> adapter() {
return this.paramAdapter;
}
public @NotNull ValueType<V> paramType() {
return adapter().type();
}
/**
* @return Value's parser, parse base object to value.
*/
public @Nullable ValueParser<V> parser() {
return parserFor(adapter());
}
/**
* @return Value's serializer, parse value to base object.
*/
public @Nullable ValueSerializer<V> serializer() {
return serializerFor(adapter());
}
private @NotNull List<V> createList() {
return constructor.get();
}
@Override
public @NotNull List<V> get() {
if (!cacheExpired()) return getCachedOrDefault(createList());
// Data that is outdated and needs to be parsed again.
List<V> list = createList();
List<?> data = config().contains(path()) ? config().getList(path()) : null;
if (data == null) return getDefaultFirst(list);
ValueParser<V> parser = parser();
if (parser == null) return getDefaultFirst(list);
for (Object dataVal : data) {
if (dataVal == null) continue;
try {
list.add(parser.parse(holder(), paramType(), dataVal));
} catch (Exception e) {
e.printStackTrace();
}
}
return updateCache(list);
}
@Override
public void set(@Nullable List<V> value) {
updateCache(value);
if (value == null) {
setData(null);
return;
}
ValueSerializer<V> serializer = serializer();
if (serializer == null) return;
List<Object> data = new ArrayList<>();
for (V val : value) {
if (val == null) continue;
try {
data.add(serializer.serialize(holder(), paramType(), val));
} catch (Exception ex) {
ex.printStackTrace();
}
}
setData(data);
super(manifest, constructor, paramAdapter);
}
@Override
@@ -114,21 +49,8 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements Lis
return resolve().get(index);
}
public @NotNull List<V> copy() {
return new ArrayList<>(resolve());
}
public <T> @NotNull T handle(Function<List<V>, T> function) {
List<V> list = resolve();
T result = function.apply(list);
set(list);
return result;
}
public @NotNull ConfiguredList<V> modify(Consumer<List<V>> consumer) {
List<V> list = resolve();
consumer.accept(list);
set(list);
@Override
public @NotNull ConfiguredList<V> self() {
return this;
}
@@ -137,89 +59,22 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements Lis
return handle(list -> list.set(index, element));
}
@Override
public int size() {
return resolve().size();
}
@Override
public boolean isEmpty() {
return resolve().isEmpty();
}
@Override
public boolean contains(Object o) {
return resolve().contains(o);
}
@NotNull
@Override
public Iterator<V> iterator() {
return resolve().iterator();
}
@NotNull
@Override
public Object @NotNull [] toArray() {
return resolve().toArray();
}
@NotNull
@Override
public <T> T @NotNull [] toArray(@NotNull T[] a) {
return resolve().toArray(a);
}
@Override
public boolean containsAll(@NotNull Collection<?> c) {
return new HashSet<>(resolve()).containsAll(c);
}
@Override
public boolean add(V v) {
handle(list -> list.add(v));
return true;
}
@Override
public void add(int index, V element) {
modify(list -> list.add(index, element));
}
@Override
public boolean addAll(@NotNull Collection<? extends V> c) {
return handle(list -> list.addAll(c));
}
@Override
public boolean addAll(int index, @NotNull Collection<? extends V> c) {
return handle(list -> list.addAll(index, c));
}
@Override
public boolean remove(Object o) {
return handle(list -> list.remove(o));
}
@Override
public V remove(int index) {
return handle(list -> list.remove(index));
}
@Override
public boolean removeAll(@NotNull Collection<?> c) {
return handle(list -> list.removeAll(c));
}
@Override
public boolean retainAll(@NotNull Collection<?> c) {
return handle(list -> list.retainAll(c));
}
@Override
public void clear() {
modify(List::clear);
}
@Override
public int indexOf(Object o) {
@@ -249,4 +104,5 @@ public class ConfiguredList<V> extends CachedConfigValue<List<V>> implements Lis
return resolve().subList(fromIndex, toIndex);
}
}
}
@@ -5,6 +5,7 @@ import cc.carm.lib.configuration.adapter.ValueParser;
import cc.carm.lib.configuration.adapter.ValueSerializer;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.map.ConfigMapCreator;
import cc.carm.lib.configuration.builder.map.SourceMapBuilder;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import cc.carm.lib.configuration.value.ValueManifest;
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
@@ -17,7 +18,11 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements Map<K, V> {
public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>, V> implements Map<K, V> {
public static <V> ConfigMapCreator<String, V> builderOf(@NotNull Class<V> valueType) {
return builderOf(String.class, valueType);
}
public static <K, V> ConfigMapCreator<K, V> builderOf(@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
return new ConfigMapCreator<>(keyType, valueType);
@@ -27,6 +32,16 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
return new ConfigMapCreator<>(ValueType.of(keyType), ValueType.of(valueType));
}
public static @NotNull <K, V>
SourceMapBuilder<LinkedHashMap<K, V>, Object, K, V> with(@NotNull Class<K> keyType, @NotNull Class<V> valueType) {
return with(ValueType.of(keyType), ValueType.of(valueType));
}
public static @NotNull <K, V>
SourceMapBuilder<LinkedHashMap<K, V>, Object, K, V> with(@NotNull ValueType<K> keyType, @NotNull ValueType<V> valueType) {
return new ConfigMapCreator<>(keyType, valueType).asLinkedMap().fromObject();
}
public static <K, V> ConfiguredMap<K, V> of(@NotNull Supplier<? extends Map<K, V>> constructor,
@NotNull ValueAdapter<K> keyAdapter, @NotNull ValueAdapter<V> valueAdapter) {
return new ConfiguredMap<>(new ValueManifest<>(new ValueType<Map<K, V>>() {
@@ -38,7 +53,7 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
protected final @NotNull ValueAdapter<K> keyAdapter;
protected final @NotNull ValueAdapter<V> valueAdapter;
public ConfiguredMap(@NotNull ValueManifest<Map<K, V>> manifest,
public ConfiguredMap(@NotNull ValueManifest<Map<K, V>, V> manifest,
@NotNull Supplier<? extends Map<K, V>> constructor,
@NotNull ValueAdapter<K> keyAdapter, @NotNull ValueAdapter<V> valueAdapter) {
super(manifest);
@@ -73,26 +88,31 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
// If the value is expired, we need to update it
Map<K, V> map = createMap();
ConfigureSection section = config().getSection(path());
if (section == null) return getDefaultFirst(map);
try {
ConfigureSection section = config().getSection(path());
if (section == null) return getDefaultFirst(map);
Set<String> keys = section.getKeys(false);
if (keys.isEmpty()) return getDefaultFirst(map);
Set<String> keys = section.getKeys(false);
if (keys.isEmpty()) return getDefaultFirst(map);
ValueParser<K> keyParser = parserFor(keyAdapter);
ValueParser<V> valueParser = parserFor(valueAdapter);
if (keyParser == null || valueParser == null) return getDefaultFirst(map);
ValueParser<K> keyParser = parserFor(keyAdapter);
if (keyParser == null) return getDefaultFirst(map);
ValueParser<V> valueParser = parserFor(valueAdapter);
if (valueParser == null) return getDefaultFirst(map);
for (String dataKey : keys) {
Object dataVal = section.get(dataKey);
if (dataVal == null) continue;
try {
K key = keyParser.parse(holder(), keyType(), dataKey);
V value = valueParser.parse(holder(), valueType(), dataVal);
map.put(key, value);
} catch (Exception e) {
e.printStackTrace();
for (String dataKey : keys) {
Object dataVal = section.get(dataKey);
if (dataVal == null) continue;
try {
K key = keyParser.parse(holder(), keyType(), dataKey);
V value = valueParser.parse(holder(), valueType(), dataVal);
map.put(key, withValidated(value));
} catch (Exception e) {
throwing(path + "." + dataKey, e);
}
}
} catch (Exception ex) {
throwing(ex);
}
return updateCache(map);
@@ -115,23 +135,28 @@ public class ConfiguredMap<K, V> extends CachedConfigValue<Map<K, V>> implements
return;
}
ValueSerializer<K> keySerializer = serializerFor(keyAdapter);
ValueSerializer<V> valueSerializer = serializerFor(valueAdapter);
if (keySerializer == null || valueSerializer == null) return;
try {
ValueSerializer<K> keySerializer = serializerFor(keyAdapter);
if (keySerializer == null) return;
ValueSerializer<V> valueSerializer = serializerFor(valueAdapter);
if (valueSerializer == null) return;
Map<Object, Object> data = new LinkedHashMap<>();
Map<Object, Object> data = new LinkedHashMap<>();
for (Map.Entry<K, V> entry : value.entrySet()) {
try {
data.put(
for (Map.Entry<K, V> entry : value.entrySet()) {
try {
data.put(
keySerializer.serialize(holder(), keyType(), entry.getKey()),
valueSerializer.serialize(holder(), valueType(), entry.getValue())
);
} catch (Exception e) {
e.printStackTrace();
valueSerializer.serialize(holder(), valueType(), withValidated(entry.getValue()))
);
} catch (Exception e) {
throwing(path + "." + entry.getKey(), e);
}
}
setData(data);
} catch (Exception ex) {
throwing(ex);
}
setData(data);
}
public <T> @NotNull T handle(Function<Map<K, V>, T> function) {
@@ -5,6 +5,8 @@ import cc.carm.lib.configuration.adapter.ValueParser;
import cc.carm.lib.configuration.adapter.ValueSerializer;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.value.ConfigValueBuilder;
import cc.carm.lib.configuration.builder.value.SourceValueBuilder;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.value.ValueManifest;
import cc.carm.lib.configuration.value.impl.CachedConfigValue;
import org.jetbrains.annotations.NotNull;
@@ -12,16 +14,54 @@ import org.jetbrains.annotations.Nullable;
import java.util.function.Supplier;
public class ConfiguredValue<V> extends CachedConfigValue<V> {
public class ConfiguredValue<V> extends CachedConfigValue<V, V> {
/**
* Create a new value builder.
*
* @param type The type of the value.
* @param <V> The type of the value.
* @return a {@link ConfigValueBuilder} with the specified type.
*/
public static <V> ConfigValueBuilder<V> builderOf(@NotNull Class<V> type) {
return new ConfigValueBuilder<>(ValueType.of(type));
}
/**
* Create a new value builder.
*
* @param type The type of the value.
* @param <V> The type of the value.
* @return a {@link ConfigValueBuilder} with the specified type.
*/
public static <V> ConfigValueBuilder<V> builderOf(@NotNull ValueType<V> type) {
return new ConfigValueBuilder<>(type);
}
/**
* Create a new value builder with the specified {@link ConfigurationHolder#registeredValues()} type.
*
* @param registeredType The type of the value.
* @param <V> The type of the value.
* @return a {@link SourceValueBuilder} with the specified registered type.
* @see ValueAdapter
*/
public static <V> SourceValueBuilder<V, V> with(@NotNull Class<V> registeredType) {
return with(ValueType.of(registeredType));
}
/**
* Create a new value builder with the specified {@link ConfigurationHolder#registeredValues()} type.
*
* @param registeredType The type of the value.
* @param <V> The type of the value.
* @return a {@link SourceValueBuilder} with the specified registered type.
* @see ValueAdapter
*/
public static <V> SourceValueBuilder<V, V> with(@NotNull ValueType<V> registeredType) {
return new ConfigValueBuilder<>(registeredType).from(registeredType);
}
public static <V> ConfiguredValue<V> of(@NotNull V defaults) {
return of(ValueType.of(defaults), () -> defaults);
}
@@ -45,13 +85,13 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
public static <V> ConfiguredValue<V> of(@NotNull ValueType<V> type, @NotNull Supplier<@Nullable V> defaultSupplier) {
return of(
new ValueManifest<>(type, defaultSupplier),
(provider, t, data) -> provider.deserialize(type, data),
(provider, t, value) -> provider.serialize(value)
new ValueManifest<>(type, defaultSupplier),
(provider, t, data) -> provider.deserialize(type, data),
(provider, t, value) -> provider.serialize(value)
);
}
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V> manifest,
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V, V> manifest,
@Nullable ValueParser<V> parser,
@Nullable ValueSerializer<V> serializer) {
ValueAdapter<V> adapter = new ValueAdapter<>(manifest.type());
@@ -60,13 +100,13 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
return of(manifest, adapter);
}
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V> manifest, @NotNull ValueAdapter<V> adapter) {
public static <V> ConfiguredValue<V> of(@NotNull ValueManifest<V, V> manifest, @NotNull ValueAdapter<V> adapter) {
return new ConfiguredValue<>(manifest, adapter);
}
protected final @NotNull ValueAdapter<V> adapter;
public ConfiguredValue(@NotNull ValueManifest<V> manifest, @NotNull ValueAdapter<V> adapter) {
public ConfiguredValue(@NotNull ValueManifest<V, V> manifest, @NotNull ValueAdapter<V> adapter) {
super(manifest);
this.adapter = adapter;
}
@@ -97,18 +137,19 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
if (!cacheExpired()) return getCachedOrDefault();
// Data that is outdated and needs to be parsed again.
Object data = getData();
if (data == null) return defaults();
ValueParser<V> parser = parser();
if (parser == null) return defaults(); // No parser, return default value.
try {
Object data = getData();
if (data == null) return defaults();
ValueParser<V> parser = parser();
if (parser == null) return defaults(); // No parser, return default value.
// If there are no errors, update the cache and return.
return updateCache(parser.parse(holder(), type(), data));
V parsed = parser.parse(holder(), type(), data);
return updateCache(withValidated(parsed));
} catch (Exception e) {
// There was a parsing error, prompted and returned the default value.
e.printStackTrace();
// There was a validate or parsing error, prompted and returned the default value.
throwing(e);
return defaults();
}
@@ -121,20 +162,20 @@ public class ConfiguredValue<V> extends CachedConfigValue<V> {
* @param value The value to be set
*/
@Override
public void set(V value) {
public void set(@Nullable V value) {
updateCache(value); // Update cache
if (value == null) {
setData(null);
return;
}
ValueSerializer<V> serializer = serializer();
if (serializer == null) return; // No serializer, do nothing.
try {
setData(serializer.serialize(holder(), type(), value));
ValueSerializer<V> serializer = serializer();
if (serializer == null) return; // No serializer, do nothing.
setData(serializer.serialize(holder(), type(), withValidated(value)));
} catch (Exception e) {
e.printStackTrace();
throwing(e);
}
}
@@ -9,7 +9,7 @@
//import java.util.LinkedList;
//import java.util.List;
//
///**
/// **
// * @author Chris2018998
// */
//public class OffsetUtil {
@@ -25,15 +25,15 @@
// } catch (NoSuchFieldException | IllegalAccessException e) {
// e.printStackTrace();
// }
//// try {
//// unsafe = AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> {
//// Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
//// theUnsafe.setAccessible(true);
//// return (Unsafe) theUnsafe.get(null);
//// });
//// } catch (Throwable e) {
//// System.err.println("Unable to load unsafe");
//// }
/// / try {
/// / unsafe = AccessController.doPrivileged((PrivilegedExceptionAction<Unsafe>) () -> {
/// / Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
/// / theUnsafe.setAccessible(true);
/// / return (Unsafe) theUnsafe.get(null);
/// / });
/// / } catch (Throwable e) {
/// / System.err.println("Unable to load unsafe");
/// / }
// }
//
// public static List<FieldOffset> getClassMemberOffset(Class<?> beanClass) {
+175 -12
View File
@@ -3,9 +3,9 @@
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>
<artifactId>configured-parent</artifactId>
<groupId>cc.carm.lib</groupId>
<version>4.0.1</version>
<version>4.2.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<properties>
@@ -13,34 +13,132 @@
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.javadoc.skip>true</maven.javadoc.skip>
<maven.deploy.skip>true</maven.deploy.skip>
<deps.mysql-driver.version>8.0.33</deps.mysql-driver.version>
<log4j.version>2.26.0</log4j.version>
</properties>
<artifactId>easyconfiguration-demo</artifactId>
<artifactId>configured-demo</artifactId>
<packaging>jar</packaging>
<name>Configured - Demo</name>
<url>https://github.com/CarmJos/configured</url>
<description>Demonstrate &amp; tests module.</description>
<dependencies>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-core</artifactId>
<artifactId>configured-core</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>easyconfiguration-feature-commentable</artifactId>
<artifactId>configured-feature-commentable</artifactId>
<version>${project.parent.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-feature-versioned</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-feature-validators</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-feature-kotlin</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-yaml</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-gson</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-sql</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cc.carm.lib</groupId>
<artifactId>easysql-beecp</artifactId>
<version>0.4.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${deps.mysql-driver.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-mongodb</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.parent.groupId}</groupId>
<artifactId>configured-hocon</artifactId>
<version>${project.parent.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
@@ -53,7 +151,72 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<source>src/main/java</source>
<source>target/generated-sources/annotations</source>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<execution>
<id>default-testCompile</id>
<phase>none</phase>
</execution>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
</project>
@@ -3,31 +3,31 @@ package cc.carm.lib.configuration.demo;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.ConfigPath;
import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
@HeaderComments({"", "数据库配置", " 用于提供数据库连接,进行数据库操作。"})
public class DatabaseConfiguration implements Configuration {
public interface DatabaseConfiguration extends Configuration {
@ConfigPath("driver")
@HeaderComments({
"数据库驱动配置,请根据数据库类型设置。",
"- MySQL(旧): com.mysql.jdbc.Driver",
"- MySQL(新): com.mysql.cj.jdbc.Driver",
"- MariaDB(推荐): org.mariadb.jdbc.Driver",
"数据库驱动配置,请根据数据库类型设置。",
"- MySQL(旧): com.mysql.jdbc.Driver",
"- MySQL(新): com.mysql.cj.jdbc.Driver",
"- MariaDB(推荐): org.mariadb.jdbc.Driver",
})
protected static final ConfigValue<String> DRIVER_NAME = ConfiguredValue.of(
String.class, "com.mysql.cj.jdbc.Driver"
ConfiguredValue<String> DRIVER_NAME = ConfiguredValue.of(
String.class, "com.mysql.cj.jdbc.Driver"
);
protected static final ConfigValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
protected static final ConfigValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
protected static final ConfigValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
protected static final ConfigValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
protected static final ConfigValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
protected static final ConfigValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
ConfiguredValue<String> HOST = ConfiguredValue.of(String.class, "127.0.0.1");
ConfiguredValue<Integer> PORT = ConfiguredValue.of(Integer.class, 3306);
ConfiguredValue<String> DATABASE = ConfiguredValue.of(String.class, "minecraft");
ConfiguredValue<String> USERNAME = ConfiguredValue.of(String.class, "root");
ConfiguredValue<String> PASSWORD = ConfiguredValue.of(String.class, "password");
protected static String buildJDBC() {
ConfiguredValue<String> EXTRA = ConfiguredValue.of(String.class, "?useSSL=false");
static String buildJDBC() {
return String.format("jdbc:mysql://%s:%s/%s%s", HOST.get(), PORT.get(), DATABASE.get(), EXTRA.get());
}
@@ -1,11 +1,13 @@
package cc.carm.lib.configuration.demo.tests;
import cc.carm.lib.configuration.demo.tests.conf.DemoConfiguration;
import cc.carm.lib.configuration.demo.tests.conf.KotlinConfiguration;
import cc.carm.lib.configuration.demo.tests.conf.RegistryConfig;
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import org.jetbrains.annotations.TestOnly;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -69,17 +71,34 @@ public class ConfigurationTest {
provider.initialize(TEST);
System.out.println("> Test Inner value before:");
System.out.println(TEST.INSTANCE.INNER_VALUE.resolve());
System.out.println(TEST.INSTANCE.STATUS.resolve());
double after = Math.random() * 200D;
System.out.println("> Test Inner value -> " + after);
TEST.INSTANCE.INNER_VALUE.set(after);
TEST.INSTANCE.STATUS.set(after);
System.out.println("> Test Inner value after:");
System.out.println(TEST.INSTANCE.INNER_VALUE.resolve());
System.out.println(TEST.INSTANCE.STATUS.resolve());
}
public static void testKotlin(ConfigurationHolder<?> provider) {
provider.initialize(KotlinConfiguration.class);
System.out.println("> Test Kotlin value before:");
System.out.println(KotlinConfiguration.INSTANCE.getLINKED_MAP().get());
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put("Language", "Kotlin");
System.out.println("> Test Kotlin value -> " + map);
KotlinConfiguration.INSTANCE.getLINKED_MAP().set(map);
System.out.println("> Test Kotlin value after:");
System.out.println(KotlinConfiguration.INSTANCE.getLINKED_MAP().get());
System.out.println("> Test Set value -> " + KotlinConfiguration.INSTANCE.getBLACK_LIST().get());
}
public static void save(ConfigurationHolder<?> provider) {
try {
provider.save();
@@ -1,14 +1,9 @@
package cc.carm.lib.configuration.demo.tests.conf;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.ConfigPath;
import cc.carm.lib.configuration.annotation.FooterComments;
import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.annotation.InlineComment;
import cc.carm.lib.configuration.demo.DatabaseConfiguration;
import cc.carm.lib.configuration.annotation.*;
import cc.carm.lib.configuration.demo.tests.model.ItemStack;
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.standard.ConfiguredList;
import cc.carm.lib.configuration.value.standard.ConfiguredMap;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
@@ -21,49 +16,52 @@ import java.util.UUID;
@ConfigPath(root = true)
@HeaderComments({"此处内容将显示在配置文件的最上方"})
@FooterComments({
"------------------------------------------------",
"此处内容将显示在配置文件的最下方",
"可用于显示版权信息等",
"感谢您使用 https://github.com/CarmJos/EasyConfiguration !"
"------------------------------------------------",
"此处内容将显示在配置文件的最下方",
"可用于显示版权信息等",
"感谢您使用 https://github.com/CarmJos/configured !"
})
public interface DemoConfiguration extends Configuration {
@ConfigPath(root = true)
ConfigValue<Double> VERSION = ConfiguredValue.of(Double.class, 1.0D);
@ConfigVersion(2)
ConfiguredValue<Double> VERSION = ConfiguredValue.of(Double.class, 2.0D);
@ConfigPath(root = true)
@FooterComments({"此处内容将显示在配置条目的下方", "可用于补充说明,但一般不建议使用"})
ConfigValue<Long> TEST_NUMBER = ConfiguredValue.of(1000000L);
ConfiguredValue<Long> TEST_NUMBER = ConfiguredValue.with(Long.class).defaults(123456789L).build();
@HeaderComments({"枚举类型测试"})
@FooterComments({"上述的枚举内容本质上是通过STRING解析的"})
ConfigValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS);
ConfiguredValue<ChronoUnit> TEST_ENUM = ConfiguredValue.of(ChronoUnit.class, ChronoUnit.DAYS);
// 支持通过 Class<?> 变量标注子配置,一并注册。
// 注意: 若对应类也有注解,则优先使用类的注解。
Class<?> DATABASE = DatabaseConfiguration.class;
@HeaderComments({"空值测试"})
@InlineComment("空值Inline注释")
ConfiguredMap<String, String> EMPTY = ConfiguredMap.builderOf(String.class, String.class)
.asLinkedMap().fromString()
.build();
@ConfigPath("registered_users") // 通过注解规定配置文件中的路径,若不进行注解则以变量名自动生成。
@HeaderComments({"Section类型数据测试"}) // 通过注解给配置添加注释。
@InlineComment("默认地注释会加到Section的首行末尾") // 通过注解给配置添加注释。
ConfiguredList<UserRecord> ALLOWLISTS = ConfiguredList.builderOf(UserRecord.class).fromSection()
.parse(UserRecord::deserialize).serialize(UserRecord::serialize)
.defaults(UserRecord.CARM).build();
.parse(UserRecord::deserialize).serialize(UserRecord::serialize)
.defaults(UserRecord.CARM).build();
@HeaderComments({
"------------------------------------------------",
"[ID - ItemStack]对照表", "", "用于测试Map类型的解析与序列化保存"
"------------------------------------------------",
"[ID - ItemStack]对照表", "", "用于测试Map类型的解析与序列化保存"
})
@FooterComments("------------------------------------------------")
ConfiguredMap<Integer, ItemStack> ITEMS = ConfiguredMap.builderOf(Integer.class, ItemStack.class)
.asLinkedMap().fromSection()
.parseKey(data -> Integer.parseInt(data))
.parse(ItemStack::deserialize).serialize(ItemStack::serialize)
.defaults(m -> {
m.put(1, new ItemStack("stone", 64));
m.put(2, new ItemStack("iron", 64, "铁锭", Arrays.asList("一些铁锭", "可以制造东西")));
})
.build();
.asLinkedMap().fromSection()
.parseKey(data -> Integer.parseInt(data))
.parse(ItemStack::deserialize).serialize(ItemStack::serialize)
.defaults(m -> {
m.put(1, new ItemStack("stone", 64));
m.put(2, new ItemStack("iron", 64, "铁锭", Arrays.asList("一些铁锭", "可以制造东西")));
})
.build();
/**
@@ -73,19 +71,19 @@ public interface DemoConfiguration extends Configuration {
class SUB implements Configuration {
@ConfigPath(value = "uuid-value", root = true)
public static final ConfigValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
.builderOf(UUID.class).fromString()
.parse((holder, data) -> UUID.fromString(data))
.build();
public static final ConfiguredValue<UUID> UUID_CONFIG_VALUE = ConfiguredValue
.builderOf(UUID.class).fromString()
.parse((holder, data) -> UUID.fromString(data))
.build();
@HeaderComments({"内部类的内部类测试", "通过这种方式,您可以轻易实现多层次的配置文件结构"})
@FooterComments({"-------------"})
public interface That extends Configuration {
ConfiguredList<UUID> OPERATORS = ConfiguredList
.builderOf(UUID.class).fromString()
.parse(s -> Objects.requireNonNull(UUID.fromString(s)))
.build();
.builderOf(UUID.class).fromString()
.parse(s -> Objects.requireNonNull(UUID.fromString(s)))
.build();
}
}
@@ -2,12 +2,13 @@ package cc.carm.lib.configuration.demo.tests.conf;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.annotation.ValueRange;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
@HeaderComments("Inner Test")
@HeaderComments("service")
public class InstanceConfig implements Configuration {
public final ConfigValue<Double> INNER_VALUE = ConfiguredValue.of(1.0D);
@ValueRange(min = 0, max = 100, message = "The value must be between 0 and 100")
public final ConfiguredValue<Double> STATUS = ConfiguredValue.of(1.0D);
}
@@ -0,0 +1,36 @@
package cc.carm.lib.configuration.demo.tests.conf
import cc.carm.lib.configuration.Configuration
import cc.carm.lib.configuration.annotation.ConfigPath
import cc.carm.lib.configuration.annotation.ConfigVersion
import cc.carm.lib.configuration.kotlin.value.*
@ConfigPath(root = true)
object KotlinConfiguration : Configuration {
@ConfigVersion(1)
val VERSION = valueFrom(Double::class) {
defaults(1.0)
}
val USER_LIST = listFrom(String::class) {
defaults("Carm Jos")
}
val BLACK_LIST = setFrom(String::class) {
defaults("404", "404", "123")
}
val NICKNAME = mapFrom(String::class, ::mutableMapOf) {
defaultMap(mapOf("Carm Jos" to "Carm"))
parse { v -> v }
serialize { v -> v }
}
val LINKED_MAP = linkedMapFrom(String::class) {
parse { value ->
value
}
serialize { v -> v }
defaults("key", "value")
}
}
@@ -6,7 +6,6 @@ import cc.carm.lib.configuration.annotation.FooterComments;
import cc.carm.lib.configuration.annotation.HeaderComments;
import cc.carm.lib.configuration.annotation.InlineComment;
import cc.carm.lib.configuration.demo.tests.model.UserRecord;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import java.util.UUID;
@@ -20,10 +19,10 @@ public class RegistryConfig implements Configuration {
@FooterComments({"12313213212"})
@InlineComment(value = "用户名(匹配注释)", regex = "name") // 通过注解给配置添加注释。
@InlineComment(value = "信息", regex = {"info.*", "info.game.*"}) // 通过注解给配置添加注释。
public final ConfigValue<UserRecord> TEST_MODEL = ConfiguredValue.builderOf(UserRecord.class).fromSection()
.defaults(new UserRecord("Carm", UUID.randomUUID()))
.parse((holder, section) -> UserRecord.deserialize(section))
.serialize((holder, data) -> data.serialize()).build();
public final ConfiguredValue<UserRecord> OWNER = ConfiguredValue.builderOf(UserRecord.class).fromSection()
.defaults(new UserRecord("Carm", UUID.randomUUID()))
.parse((holder, section) -> UserRecord.deserialize(section))
.serialize((holder, data) -> data.serialize()).build();
}
@@ -33,11 +33,11 @@ public class ItemStack {
return amount;
}
public String getName() {
public @Nullable String getName() {
return name;
}
public List<String> getLore() {
public @Nullable List<String> getLore() {
return lore;
}
@@ -49,11 +49,11 @@ public class ItemStack {
this.amount = amount;
}
public void setName(String name) {
public void setName(@Nullable String name) {
this.name = name;
}
public void setLore(List<String> lore) {
public void setLore(@Nullable List<String> lore) {
this.lore = lore;
}
@@ -69,10 +69,10 @@ public class ItemStack {
public static ItemStack deserialize(ConfigureSection section) {
return new ItemStack(
section.getString("material"),
section.getInt("amount", 1),
section.getString("name"),
section.getStringList("lore")
section.getString("material"),
section.getInt("amount", 1),
section.getString("name"),
section.getStringList("lore")
);
}
}
@@ -36,6 +36,7 @@ public class UserRecord extends AbstractRecord {
}
public static UserRecord deserialize(ConfigureSection section) {
System.out.println("> Deserializing -> " + section.fullPath());
String name = section.getString("name");
if (name == null) throw new NullPointerException("name is null");
String uuidString = section.getString("info.uuid");
@@ -51,8 +52,8 @@ public class UserRecord extends AbstractRecord {
@Override
public String toString() {
return "TestUser{" +
"name='" + name + '\'' +
", uuid=" + uuid +
'}';
"name='" + name + '\'' +
", uuid=" + uuid +
'}';
}
}
}
@@ -1,29 +1,21 @@
package config;
package cc.carm.lib.configuration.tests.test;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.value.ConfigValue;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
import org.junit.Test;
import java.io.File;
public class JSONConfigTest {
public class JSONConfigTests {
protected final ConfigurationHolder<?> holder = JSONConfigFactory
.from(new File("target"), "config.json")
.resourcePath("example.json")
.build();
.from(new File("target"), "config.json")
.resourcePath("example.json")
.build();
@Test
public void onTest() {
ConfigValue<Boolean> EXAMPLE = ConfiguredValue.of(false);
EXAMPLE.initialize(this.holder, "example");
System.out.println("Example: " + EXAMPLE.get());
ConfigurationTest.testDemo(this.holder);
ConfigurationTest.testInner(this.holder);
@@ -1,28 +1,29 @@
package yaml.test;
package cc.carm.lib.configuration.tests.test;
import cc.carm.lib.configuration.commentable.CommentableMeta;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory;
import cc.carm.lib.configuration.source.yaml.YAMLSource;
import cc.carm.lib.configuration.validators.Validators;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class YamlTests {
@Test
public void test() {
public void test() throws Exception {
ConfigurationHolder<YAMLSource> holder = YAMLConfigFactory.from("target/tests.yml")
.resourcePath("configs/sample.yml").build();
.resourcePath("configs/sample.yml").build();
Validators.activate(holder);
ConfigurationTest.testDemo(holder);
ConfigurationTest.testInner(holder);
Map<String, List<String>> headers = holder.extractMetadata(CommentableMeta.HEADER);
System.out.println("Header comments: ");
headers.forEach((k, v) -> {
@@ -43,5 +44,13 @@ public class YamlTests {
ConfigurationTest.save(holder);
}
@Test
public void testKotlin() {
ConfigurationHolder<YAMLSource> holder = YAMLConfigFactory.from("target/kotlin.yml").build();
ConfigurationTest.testKotlin(holder);
ConfigurationTest.save(holder);
}
}
@@ -0,0 +1,12 @@
package cc.carm.lib.configuration.tests.test.mongodb;
import cc.carm.lib.configuration.Configuration;
import cc.carm.lib.configuration.value.standard.ConfiguredValue;
interface MongoConfig extends Configuration {
ConfiguredValue<String> HOST = ConfiguredValue.of("127.0.0.1");
ConfiguredValue<Integer> PORT = ConfiguredValue.of(27017);
ConfiguredValue<String> USERNAME = ConfiguredValue.of("minecraft");
ConfiguredValue<String> PASSWORD = ConfiguredValue.of("minecraft");
ConfiguredValue<String> DATABASE = ConfiguredValue.of("minecraft");
}
@@ -0,0 +1,57 @@
package cc.carm.lib.configuration.tests.test.mongodb;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
import cc.carm.lib.configuration.source.mongodb.MongoConfigFactory;
import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import org.bson.UuidRepresentation;
import org.junit.Test;
import java.io.File;
public class MongoTest {
boolean local = false;
@Test
public void test() {
if (!local) return;
ConfigurationHolder<?> gsonHolder = JSONConfigFactory.from(new File("target/mongo.json")).build();
gsonHolder.initialize(MongoConfig.class);
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(
"mongodb://" + MongoConfig.HOST.resolve() + ":" + MongoConfig.PORT.resolve()
))
.credential(MongoCredential.createCredential(
MongoConfig.USERNAME.resolve(), MongoConfig.DATABASE.resolve(),
MongoConfig.PASSWORD.resolve().toCharArray()
))
.uuidRepresentation(UuidRepresentation.STANDARD)
.build();
MongoClient mongoClient = MongoClients.create(settings);
MongoDatabase mongoDatabase = mongoClient.getDatabase(MongoConfig.DATABASE.resolve());
ConfigurationHolder<?> mongoHolder = MongoConfigFactory
.from(mongoDatabase, "configs")
.namespace("my_plugin")
.build();
// Test the configuration
ConfigurationTest.testDemo(mongoHolder);
ConfigurationTest.testInner(mongoHolder);
ConfigurationTest.save(mongoHolder);
}
}
@@ -0,0 +1,48 @@
package cc.carm.lib.configuration.tests.test.sql;
import cc.carm.lib.configuration.demo.DatabaseConfiguration;
import cc.carm.lib.configuration.demo.tests.ConfigurationTest;
import cc.carm.lib.configuration.source.ConfigurationHolder;
import cc.carm.lib.configuration.source.json.JSONConfigFactory;
import cc.carm.lib.configuration.source.sql.SQLConfigFactory;
import cc.carm.lib.easysql.EasySQL;
import cc.carm.lib.easysql.api.SQLManager;
import cc.carm.lib.easysql.beecp.BeeDataSourceConfig;
import org.junit.Test;
import java.io.File;
public class SQLConfigTest {
boolean local = false;
@Test
public void test() {
if (!local) return;
ConfigurationHolder<?> gsonHolder = JSONConfigFactory.from(new File("target/sql.json")).build();
gsonHolder.initialize(DatabaseConfiguration.class);
BeeDataSourceConfig config = new BeeDataSourceConfig();
config.setDriverClassName(DatabaseConfiguration.DRIVER_NAME.resolve());
config.setJdbcUrl(DatabaseConfiguration.buildJDBC());
config.setUsername(DatabaseConfiguration.USERNAME.resolve());
config.setPassword(DatabaseConfiguration.PASSWORD.resolve());
SQLManager manager = EasySQL.createManager(config);
manager.setDebugMode(true);
ConfigurationHolder<?> holder = SQLConfigFactory.from(manager)
.tableName("test_configs")
.namespace("testing")
.build();
ConfigurationTest.testDemo(holder);
ConfigurationTest.testInner(holder);
ConfigurationTest.save(holder);
EasySQL.shutdownManager(manager);
}
}
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="config.SQLConfigTest">
<Configuration status="WARN" packages="cc.carm.lib.configuration.tests">
<Appenders>
<console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
+64
View File
@@ -0,0 +1,64 @@
<?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>
<parent>
<groupId>cc.carm.lib</groupId>
<artifactId>configured-parent</artifactId>
<version>4.2.1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<properties>
<maven.compiler.source>${project.jdk.version}</maven.compiler.source>
<maven.compiler.target>${project.jdk.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<artifactId>configured-feature-collections</artifactId>
<packaging>jar</packaging>
<name>Configured - Collections Feature</name>
<url>https://github.com/CarmJos/configured</url>
<description>Provides more collection type support for the Configured framework.</description>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>configured-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>configured-gson</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,96 @@
package cc.carm.lib.configuration.builder.set;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.collection.SectionCollectionBuilder;
import cc.carm.lib.configuration.builder.collection.SourceCollectionBuilder;
import cc.carm.lib.configuration.function.ValueHandler;
import cc.carm.lib.configuration.source.section.ConfigureSection;
import cc.carm.lib.configuration.value.collections.ConfiguredSet;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
public class ConfigSetBuilder<V> {
protected final @NotNull ValueType<V> type;
public ConfigSetBuilder(@NotNull ValueType<V> type) {
this.type = type;
}
public <S> @NotNull SourceBuilder<S, V> from(@NotNull Class<S> sourceType) {
return from(ValueType.of(sourceType));
}
public <S> @NotNull SourceBuilder<S, V> from(@NotNull ValueType<S> sourceType) {
return new SourceBuilder<>(
sourceType, type,
ValueHandler.required(type),
ValueHandler.required(sourceType)
);
}
public @NotNull ConfigSetBuilder.SourceBuilder<Object, V> fromObject() {
return new SourceBuilder<>(
ValueType.OBJECT, type,
ValueHandler.deserialize(type), ValueHandler.toObject()
);
}
public @NotNull ConfigSetBuilder.SourceBuilder<String, V> fromString() {
return new SourceBuilder<>(
ValueType.STRING, type,
ValueHandler.required(type), ValueHandler.stringValue()
);
}
public @NotNull ConfigSetBuilder.SectionBuilder<V> fromSection() {
return new SectionBuilder<>(type, ValueHandler.required(type), ValueHandler.required());
}
public static class SourceBuilder<SOURCE, V> extends SourceCollectionBuilder<SOURCE, V, Set<V>, ConfiguredSet<V>, SourceBuilder<SOURCE, V>> {
public SourceBuilder(@NotNull ValueType<SOURCE> sourceType,
@NotNull ValueType<V> paramType,
@NotNull ValueHandler<SOURCE, V> parser,
@NotNull ValueHandler<V, SOURCE> serializer) {
super(LinkedHashSet::new, sourceType, paramType, parser, serializer);
}
@Override
protected @NotNull ConfigSetBuilder.SourceBuilder<SOURCE, V> self() {
return this;
}
@Override
public @NotNull ConfiguredSet<V> build() {
return new ConfiguredSet<>(buildManifest(), constructor, buildAdapter());
}
}
public static class SectionBuilder<V> extends SectionCollectionBuilder<V, Set<V>, ConfiguredSet<V>, SectionBuilder<V>> {
public SectionBuilder(@NotNull ValueType<V> paramType,
@NotNull ValueHandler<ConfigureSection, V> parser,
@NotNull ValueHandler<V, ? extends Map<String, Object>> serializer) {
super(LinkedHashSet::new, paramType, parser, serializer);
}
@Override
protected @NotNull ConfigSetBuilder.SectionBuilder<V> self() {
return this;
}
@Override
public @NotNull ConfiguredSet<V> build() {
return new ConfiguredSet<>(buildManifest(), constructor, buildAdapter());
}
}
}
@@ -0,0 +1,52 @@
package cc.carm.lib.configuration.value.collections;
import cc.carm.lib.configuration.adapter.ValueAdapter;
import cc.carm.lib.configuration.adapter.ValueType;
import cc.carm.lib.configuration.builder.set.ConfigSetBuilder;
import cc.carm.lib.configuration.value.ValueManifest;
import cc.carm.lib.configuration.value.impl.CollectionConfigValue;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Supplier;
public class ConfiguredSet<V> extends CollectionConfigValue<V, Set<V>, ConfiguredSet<V>> implements Set<V> {
public static <T> @NotNull ConfigSetBuilder<T> builderOf(@NotNull Class<T> type) {
return builderOf(ValueType.of(type));
}
public static <T> @NotNull ConfigSetBuilder<T> builderOf(@NotNull ValueType<T> type) {
return new ConfigSetBuilder<>(type);
}
public static <T> @NotNull ConfigSetBuilder.SourceBuilder<Object, T> with(@NotNull Class<T> registeredType) {
return with(ValueType.of(registeredType));
}
public static <T> @NotNull ConfigSetBuilder.SourceBuilder<Object, T> with(@NotNull ValueType<T> registeredType) {
return builderOf(registeredType).fromObject();
}
@SafeVarargs
public static <T> @NotNull ConfiguredSet<T> of(@NotNull T value, @NotNull T... values) {
Set<T> list = new LinkedHashSet<>();
list.add(value);
Collections.addAll(list, values);
return with(ValueType.of(value)).defaults(list).build();
}
public ConfiguredSet(@NotNull ValueManifest<Set<V>, V> manifest,
@NotNull Supplier<? extends Set<V>> constructor,
@NotNull ValueAdapter<V> paramAdapter) {
super(manifest, constructor, paramAdapter);
}
@Override
public @NotNull ConfiguredSet<V> self() {
return this;
}
}
+9 -5
View File
@@ -5,8 +5,8 @@
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cc.carm.lib</groupId>
<artifactId>easyconfiguration-parent</artifactId>
<version>4.0.1</version>
<artifactId>configured-parent</artifactId>
<version>4.2.1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<properties>
@@ -16,13 +16,17 @@
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<artifactId>easyconfiguration-feature-commentable</artifactId>
<artifactId>configured-feature-commentable</artifactId>
<packaging>jar</packaging>
<name>Configured - Commentable Feature</name>
<url>https://github.com/CarmJos/configured</url>
<description>Enables comment support for configuration files in the Configured framework.</description>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>easyconfiguration-core</artifactId>
<artifactId>configured-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
@@ -48,4 +52,4 @@
</plugins>
</build>
</project>
</project>
@@ -44,6 +44,9 @@ public @interface InlineComment {
* bar: "foobar" # Comment Contents
* </pre></blockquote>
*
* <p><i>Some implements may not support this feature
* in list values or other complex structures.</i>
*
* @return The path regexes of this comment
*/
@NotNull
@@ -9,7 +9,12 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
public @interface InlineComments {
/**
* Multiple inline comments support.
*
* @return inline comment contents.
* @see InlineComment
*/
InlineComment[] value();
}
@@ -26,7 +26,7 @@ public class Commentable {
String comment = getInlineComment(holder, path, null);
if (comment != null) return comment;
String sep = String.valueOf(holder.options().get(StandardOptions.PATH_SEPARATOR));
String sep = String.valueOf(holder.option(StandardOptions.PATH_SEPARATOR));
// If the comment is not found, try to get the comment from the parent section
String[] keys = path.split(Pattern.quote(sep));
@@ -42,13 +42,24 @@ public interface CommentableMeta {
static void register(@NotNull ConfigurationInitializer initializer) {
initializer.registerAnnotation(
HeaderComments.class, HEADER,
a -> Arrays.asList(a.value())
HeaderComments.class, HEADER,
a -> Arrays.asList(a.value())
);
initializer.registerAnnotation(
FooterComments.class, FOOTER,
a -> Arrays.asList(a.value())
FooterComments.class, FOOTER,
a -> Arrays.asList(a.value())
);
initializer.registerAnnotation(InlineComment.class, INLINE, a -> {
Map<String, String> map = new HashMap<>();
if (a.regex().length == 0) { // for current path
map.put(null, a.value());
return map;
}
for (String regex : a.regex()) { // for specified path
map.put(regex, a.value());
}
return map;
});
initializer.registerAnnotation(InlineComments.class, INLINE, a -> {
Map<String, String> map = new HashMap<>();
for (InlineComment comment : a.value()) {

Some files were not shown because too many files have changed in this diff Show More