From 7c4f630f7c9995be7356a636711bf1b7fdc64f91 Mon Sep 17 00:00:00 2001 From: Exlll Date: Thu, 7 Jul 2022 18:04:08 +0200 Subject: [PATCH] ConfigLib v3 --- ...aster-push.yml => publish-on-dispatch.yml} | 19 +- .github/workflows/test-on-pr-or-dev-push.yml | 11 +- .gitignore | 6 +- .travis.yml | 5 - .../java/de/exlll/configlib/ConfigLib.java | 5 - .../configs/yaml/BukkitYamlConfiguration.java | 56 - .../src/main/resources/plugin.yml | 5 - .../java/de/exlll/configlib/ConfigLib.java | 5 - .../src/main/resources/plugin.yml | 5 - .../java/de/exlll/configlib/Comments.java | 85 - .../de/exlll/configlib/Configuration.java | 214 --- .../exlll/configlib/ConfigurationSource.java | 32 - .../ConfigurationStoreException.java | 10 - .../java/de/exlll/configlib/Converter.java | 231 --- .../java/de/exlll/configlib/Converters.java | 551 ------- .../java/de/exlll/configlib/FieldMapper.java | 120 -- .../main/java/de/exlll/configlib/Reflect.java | 110 -- .../java/de/exlll/configlib/Validator.java | 357 ---- .../exlll/configlib/annotation/Comment.java | 32 - .../annotation/ConfigurationElement.java | 15 - .../exlll/configlib/annotation/Convert.java | 18 - .../configlib/annotation/ElementType.java | 40 - .../de/exlll/configlib/annotation/Format.java | 38 - .../exlll/configlib/annotation/NoConvert.java | 17 - .../configlib/configs/yaml/YamlComments.java | 76 - .../configs/yaml/YamlConfiguration.java | 212 --- .../configlib/configs/yaml/YamlSource.java | 138 -- .../exlll/configlib/filter/FieldFilter.java | 26 - .../exlll/configlib/filter/FieldFilters.java | 20 - .../configlib/format/FieldNameFormatter.java | 13 - .../configlib/format/FieldNameFormatters.java | 55 - .../java/de/exlll/configlib/CommentsTest.java | 54 - .../de/exlll/configlib/ConfigurationTest.java | 47 - .../configlib/FieldMapperConverterTest.java | 392 ----- .../exlll/configlib/FieldMapperHelpers.java | 91 -- .../de/exlll/configlib/FieldMapperTest.java | 592 ------- .../configlib/FieldNameFormattersTest.java | 31 - .../java/de/exlll/configlib/ReflectTest.java | 61 - .../de/exlll/configlib/ValidatorTest.java | 667 -------- .../ClassWithFinalStaticTransientField.java | 7 - .../de/exlll/configlib/classes/TestClass.java | 482 ------ .../configlib/classes/TestExcludedClass.java | 55 - .../exlll/configlib/classes/TestSubClass.java | 149 -- .../classes/TestSubClassConverter.java | 23 - .../configlib/classes/TestSubSubClass.java | 83 - .../mem/InSharedMemoryConfiguration.java | 45 - .../configs/yaml/YamlConfigurationTest.java | 941 ----------- .../configs/yaml/YamlSourceTest.java | 42 - .../configlib/filter/FieldFilterTest.java | 58 - .../configlib/util/CollectionFactory.java | 87 - README.md | 884 +++++----- build.gradle.kts | 75 +- buildSrc/build.gradle.kts | 12 + buildSrc/settings.gradle.kts | 0 .../src/main/kotlin/java-config.gradle.kts | 91 ++ configlib-core/build.gradle.kts | 8 + .../main/java/de/exlll/configlib/Comment.java | 21 + .../java/de/exlll/configlib/CommentNode.java | 29 + .../exlll/configlib/CommentNodeExtractor.java | 83 + .../de/exlll/configlib/Configuration.java | 13 + .../configlib/ConfigurationException.java | 4 +- .../configlib/ConfigurationProperties.java | 223 +++ .../configlib/ConfigurationSerializer.java | 139 ++ .../de/exlll/configlib/Configurations.java | 236 +++ .../de/exlll/configlib/FieldExtractor.java | 25 + .../de/exlll/configlib/FieldExtractors.java | 59 + .../java/de/exlll/configlib/FieldFilter.java | 15 + .../java/de/exlll/configlib/FieldFilters.java | 27 + .../de/exlll/configlib/FieldFormatter.java | 24 + .../de/exlll/configlib/FieldFormatters.java | 59 + .../FileConfigurationProperties.java | 137 ++ .../configlib/FileConfigurationStore.java | 49 + .../main/java/de/exlll/configlib/Ignore.java | 13 + .../main/java/de/exlll/configlib/Reflect.java | 110 ++ .../java/de/exlll/configlib/Serializer.java | 42 + .../exlll/configlib/SerializerSelector.java | 145 ++ .../java/de/exlll/configlib/Serializers.java | 621 +++++++ .../java/de/exlll/configlib/Validator.java | 36 + .../YamlConfigurationProperties.java | 62 + .../configlib/YamlConfigurationStore.java | 286 ++++ .../configlib/CommentNodeExtractorTest.java | 322 ++++ .../ConfigurationPropertiesTest.java | 94 ++ .../ConfigurationSerializerTest.java | 317 ++++ .../exlll/configlib/ConfigurationsTest.java | 147 ++ .../configlib/ExampleConfigurationTests.java | 673 ++++++++ .../exlll/configlib/FieldExtractorTest.java | 16 + .../exlll/configlib/FieldExtractorsTest.java | 102 ++ .../de/exlll/configlib/FieldFiltersTest.java | 41 + .../exlll/configlib/FieldFormatterTest.java | 22 + .../exlll/configlib/FieldFormattersTest.java | 58 + .../FileConfigurationPropertiesTest.java | 27 + .../java/de/exlll/configlib/ReflectTest.java | 217 +++ .../configlib/SerializerSelectorTest.java | 362 ++++ .../de/exlll/configlib/SerializersTest.java | 908 +++++++++++ .../java/de/exlll/configlib/TestUtils.java | 229 +++ .../configlib/YamlConfigurationStoreTest.java | 235 +++ .../exlll/configlib/YamlFileWriterTest.java | 418 +++++ .../ExampleConfigurationA1.java | 1418 ++++++++++++++++ .../ExampleConfigurationA2.java | 1450 +++++++++++++++++ .../ExampleConfigurationB1.java | 188 +++ .../ExampleConfigurationB2.java | 192 +++ .../ExampleConfigurationNulls.java | 184 +++ .../ExampleConfigurationsSerialized.java | 419 +++++ .../configlib/configurations/ExampleEnum.java | 5 + .../configurations/ExampleInitializer.java | 938 +++++++++++ configlib-paper/build.gradle.kts | 10 + .../java/de/exlll/configlib/ConfigLib.java | 8 + configlib-paper/src/main/resources/plugin.yml | 7 + configlib-velocity/build.gradle.kts | 11 + .../java/de/exlll/configlib/ConfigLib.java | 16 + configlib-waterfall/build.gradle.kts | 10 + .../java/de/exlll/configlib/ConfigLib.java | 8 + .../src/main/resources/plugin.yml | 6 + docs/custom-config.md | 181 -- docs/tutorial.md | 537 ------ gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 11 +- 117 files changed, 12129 insertions(+), 7622 deletions(-) rename .github/workflows/{publish-on-master-push.yml => publish-on-dispatch.yml} (59%) delete mode 100644 .travis.yml delete mode 100644 ConfigLib-Bukkit/src/main/java/de/exlll/configlib/ConfigLib.java delete mode 100644 ConfigLib-Bukkit/src/main/java/de/exlll/configlib/configs/yaml/BukkitYamlConfiguration.java delete mode 100644 ConfigLib-Bukkit/src/main/resources/plugin.yml delete mode 100644 ConfigLib-Bungee/src/main/java/de/exlll/configlib/ConfigLib.java delete mode 100644 ConfigLib-Bungee/src/main/resources/plugin.yml delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/Comments.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/Configuration.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationSource.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationStoreException.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/Converter.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/Converters.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/FieldMapper.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/Reflect.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/Validator.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Comment.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ConfigurationElement.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Convert.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ElementType.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Format.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/NoConvert.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlComments.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlConfiguration.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlSource.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/filter/FieldFilter.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/filter/FieldFilters.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatter.java delete mode 100644 ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatters.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/CommentsTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/ConfigurationTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperConverterTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperHelpers.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/FieldNameFormattersTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/ReflectTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/ValidatorTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/classes/ClassWithFinalStaticTransientField.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestClass.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestExcludedClass.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubClass.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubClassConverter.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubSubClass.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/configs/mem/InSharedMemoryConfiguration.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/configs/yaml/YamlConfigurationTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/configs/yaml/YamlSourceTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/filter/FieldFilterTest.java delete mode 100644 ConfigLib-Core/src/test/java/de/exlll/configlib/util/CollectionFactory.java create mode 100644 buildSrc/build.gradle.kts create mode 100644 buildSrc/settings.gradle.kts create mode 100644 buildSrc/src/main/kotlin/java-config.gradle.kts create mode 100644 configlib-core/build.gradle.kts create mode 100644 configlib-core/src/main/java/de/exlll/configlib/Comment.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/CommentNode.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/CommentNodeExtractor.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/Configuration.java rename {ConfigLib-Core => configlib-core}/src/main/java/de/exlll/configlib/ConfigurationException.java (63%) create mode 100644 configlib-core/src/main/java/de/exlll/configlib/ConfigurationProperties.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/ConfigurationSerializer.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/Configurations.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/FieldExtractor.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/FieldExtractors.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/FieldFilter.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/FieldFilters.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/FieldFormatter.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/FieldFormatters.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/FileConfigurationProperties.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/FileConfigurationStore.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/Ignore.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/Reflect.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/Serializer.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/Serializers.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/Validator.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/YamlConfigurationProperties.java create mode 100644 configlib-core/src/main/java/de/exlll/configlib/YamlConfigurationStore.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/CommentNodeExtractorTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/ConfigurationPropertiesTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/ConfigurationSerializerTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/ConfigurationsTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/ExampleConfigurationTests.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/FieldExtractorTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/FieldExtractorsTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/FieldFiltersTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/FieldFormatterTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/FieldFormattersTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/FileConfigurationPropertiesTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/ReflectTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/SerializerSelectorTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/SerializersTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/TestUtils.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/YamlConfigurationStoreTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/YamlFileWriterTest.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationA1.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationA2.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationB1.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationB2.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationNulls.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationsSerialized.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleEnum.java create mode 100644 configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleInitializer.java create mode 100644 configlib-paper/build.gradle.kts create mode 100644 configlib-paper/src/main/java/de/exlll/configlib/ConfigLib.java create mode 100644 configlib-paper/src/main/resources/plugin.yml create mode 100644 configlib-velocity/build.gradle.kts create mode 100644 configlib-velocity/src/main/java/de/exlll/configlib/ConfigLib.java create mode 100644 configlib-waterfall/build.gradle.kts create mode 100644 configlib-waterfall/src/main/java/de/exlll/configlib/ConfigLib.java create mode 100644 configlib-waterfall/src/main/resources/plugin.yml delete mode 100644 docs/custom-config.md delete mode 100644 docs/tutorial.md diff --git a/.github/workflows/publish-on-master-push.yml b/.github/workflows/publish-on-dispatch.yml similarity index 59% rename from .github/workflows/publish-on-master-push.yml rename to .github/workflows/publish-on-dispatch.yml index db63416..49cdccf 100644 --- a/.github/workflows/publish-on-master-push.yml +++ b/.github/workflows/publish-on-dispatch.yml @@ -1,21 +1,18 @@ -name: publish-on-master-push +name: publish-on-dispatch -on: - push: - branches: [ master ] - paths-ignore: - - '**/README.md' +on: [ 'workflow_dispatch' ] jobs: build-and-publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up JDK 16 - uses: actions/setup-java@v2 + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup JDK and Gradle + uses: actions/setup-java@v3 with: - distribution: 'adopt' - java-version: '16' + distribution: 'temurin' + java-version: '17' - name: Build with Gradle run: ./gradlew build - name: Archive test reports diff --git a/.github/workflows/test-on-pr-or-dev-push.yml b/.github/workflows/test-on-pr-or-dev-push.yml index 8c0cd2c..b0b4a41 100644 --- a/.github/workflows/test-on-pr-or-dev-push.yml +++ b/.github/workflows/test-on-pr-or-dev-push.yml @@ -12,12 +12,13 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Set up JDK 16 - uses: actions/setup-java@v2 + - name: Checkout repository + uses: actions/checkout@v3 + - name: Setup JDK and Gradle + uses: actions/setup-java@v3 with: - distribution: 'adopt' - java-version: '16' + distribution: 'temurin' + java-version: '17' - name: Test with Gradle run: ./gradlew test - name: Archive test reports diff --git a/.gitignore b/.gitignore index 790e8e0..2204874 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,8 @@ **/out **/build -**/.gradle \ No newline at end of file +**/.gradle + +.data +TODO.txt +TMP.txt \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c3c972f..0000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: java -jdk: - - oraclejdk8 - - oraclejdk9 - - oraclejdk10 diff --git a/ConfigLib-Bukkit/src/main/java/de/exlll/configlib/ConfigLib.java b/ConfigLib-Bukkit/src/main/java/de/exlll/configlib/ConfigLib.java deleted file mode 100644 index 13b20c2..0000000 --- a/ConfigLib-Bukkit/src/main/java/de/exlll/configlib/ConfigLib.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.exlll.configlib; - -import org.bukkit.plugin.java.JavaPlugin; - -public class ConfigLib extends JavaPlugin {} diff --git a/ConfigLib-Bukkit/src/main/java/de/exlll/configlib/configs/yaml/BukkitYamlConfiguration.java b/ConfigLib-Bukkit/src/main/java/de/exlll/configlib/configs/yaml/BukkitYamlConfiguration.java deleted file mode 100644 index 5ae2290..0000000 --- a/ConfigLib-Bukkit/src/main/java/de/exlll/configlib/configs/yaml/BukkitYamlConfiguration.java +++ /dev/null @@ -1,56 +0,0 @@ -package de.exlll.configlib.configs.yaml; - -import org.bukkit.configuration.file.YamlConstructor; -import org.bukkit.configuration.file.YamlRepresenter; - -import java.nio.file.Path; - -/** - * A {@code BukkitYamlConfiguration} is a specialized form of a - * {@code YamlConfiguration} that uses better default values. - */ -public abstract class BukkitYamlConfiguration extends YamlConfiguration { - protected BukkitYamlConfiguration(Path path, BukkitYamlProperties properties) { - super(path, properties); - } - - protected BukkitYamlConfiguration(Path path) { - this(path, BukkitYamlProperties.DEFAULT); - } - - public static class BukkitYamlProperties extends YamlProperties { - public static final BukkitYamlProperties DEFAULT = builder().build(); - - private BukkitYamlProperties(Builder builder) { - super(builder); - } - - public static Builder builder() { - return new Builder() { - @Override - protected Builder getThis() { - return this; - } - }; - } - - public static abstract class - Builder> - extends YamlProperties.Builder { - - protected Builder() { - setConstructor(new YamlConstructor()); - setRepresenter(new YamlRepresenter()); - } - - /** - * Builds a new {@code BukkitYamlProperties} instance using the values set. - * - * @return new {@code BukkitYamlProperties} instance - */ - public BukkitYamlProperties build() { - return new BukkitYamlProperties(this); - } - } - } -} diff --git a/ConfigLib-Bukkit/src/main/resources/plugin.yml b/ConfigLib-Bukkit/src/main/resources/plugin.yml deleted file mode 100644 index 5b27cae..0000000 --- a/ConfigLib-Bukkit/src/main/resources/plugin.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: ConfigLib -author: Exlll - -version: 2.2.0 -main: de.exlll.configlib.ConfigLib \ No newline at end of file diff --git a/ConfigLib-Bungee/src/main/java/de/exlll/configlib/ConfigLib.java b/ConfigLib-Bungee/src/main/java/de/exlll/configlib/ConfigLib.java deleted file mode 100644 index 0151534..0000000 --- a/ConfigLib-Bungee/src/main/java/de/exlll/configlib/ConfigLib.java +++ /dev/null @@ -1,5 +0,0 @@ -package de.exlll.configlib; - -import net.md_5.bungee.api.plugin.Plugin; - -public class ConfigLib extends Plugin {} diff --git a/ConfigLib-Bungee/src/main/resources/plugin.yml b/ConfigLib-Bungee/src/main/resources/plugin.yml deleted file mode 100644 index 5b27cae..0000000 --- a/ConfigLib-Bungee/src/main/resources/plugin.yml +++ /dev/null @@ -1,5 +0,0 @@ -name: ConfigLib -author: Exlll - -version: 2.2.0 -main: de.exlll.configlib.ConfigLib \ No newline at end of file diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Comments.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Comments.java deleted file mode 100644 index 35db5f1..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Comments.java +++ /dev/null @@ -1,85 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.annotation.Comment; - -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static java.util.stream.Collectors.toMap; - -/** - * Instances of this class contain all comments of a {@link Configuration} class - * and its fields. - */ -public final class Comments { - private final List classComments; - private final Map> fieldComments; - - private Comments(List classComments, - Map> fieldComments) { - this.classComments = classComments; - this.fieldComments = fieldComments; - } - - static Comments ofClass(Class cls) { - List classComments = getComments(cls); - Map> fieldComments = Arrays - .stream(cls.getDeclaredFields()) - .filter(Comments::isCommented) - .collect(toMap(Field::getName, Comments::getComments)); - return new Comments(classComments, fieldComments); - } - - private static boolean isCommented(AnnotatedElement element) { - return element.isAnnotationPresent(Comment.class); - } - - private static List getComments(AnnotatedElement element) { - Comment comment = element.getAnnotation(Comment.class); - return (comment != null) - ? Arrays.asList(comment.value()) - : Collections.emptyList(); - } - - /** - * Returns if the {@code Configuration} this {@code Comments} object belongs to - * has class comments. - * - * @return true, if {@code Configuration} has class comments. - */ - public boolean hasClassComments() { - return !classComments.isEmpty(); - } - - /** - * Returns if the {@code Configuration} this {@code Comments} object belongs to - * has field comments. - * - * @return true, if {@code Configuration} has field comments. - */ - public boolean hasFieldComments() { - return !fieldComments.isEmpty(); - } - - /** - * Returns a list of class comments. - * - * @return list of class comments - */ - public List getClassComments() { - return classComments; - } - - /** - * Returns lists of field comments mapped by field name. - * - * @return lists of field comments by field name - */ - public Map> getFieldComments() { - return fieldComments; - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Configuration.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Configuration.java deleted file mode 100644 index 501ae1d..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Configuration.java +++ /dev/null @@ -1,214 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.FieldMapper.MappingInfo; -import de.exlll.configlib.filter.FieldFilter; -import de.exlll.configlib.filter.FieldFilters; -import de.exlll.configlib.format.FieldNameFormatter; -import de.exlll.configlib.format.FieldNameFormatters; - -import java.io.IOException; -import java.util.Map; -import java.util.Objects; - -/** - * Parent class of all configurations. - *

- * This class contains the most basic methods that every configuration needs. - * - * @param type of the configuration - */ -public abstract class Configuration> { - /** - * {@code Comments} object containing all class and field comments - * of this configuration - */ - protected final Comments comments; - private final Properties props; - - /** - * Constructs a new {@code Configuration} object. - * - * @param properties {@code Properties} used to configure this configuration - * @throws NullPointerException if {@code properties} is null - */ - protected Configuration(Properties properties) { - this.props = Objects.requireNonNull(properties); - this.comments = Comments.ofClass(getClass()); - } - - /** - * Saves this {@code Configuration}. - * - * @throws ConfigurationException if any field is not properly configured - * @throws ConfigurationStoreException if an I/O error occurred while loading - * this configuration - */ - public final void save() { - try { - preSave(); - MappingInfo mappingInfo = MappingInfo.from(this); - Map map = FieldMapper - .instanceToMap(this, mappingInfo); - getSource().saveConfiguration(getThis(), map); - } catch (IOException e) { - throw new ConfigurationStoreException(e); - } - } - - /** - * Loads this {@code Configuration}. - * - * @throws ConfigurationException if values cannot be converted back to their - * original representation - * @throws ConfigurationStoreException if an I/O error occurred while loading - * this configuration - */ - public final void load() { - try { - Map map = getSource().loadConfiguration(getThis()); - MappingInfo mappingInfo = MappingInfo.from(this); - FieldMapper.instanceFromMap(this, map, mappingInfo); - postLoad(); - } catch (IOException e) { - throw new ConfigurationStoreException(e); - } - } - - /** - * Returns the {@link ConfigurationSource} used for saving and loading this - * {@code Configuration}. - * - * @return {@code ConfigurationSource} used for saving and loading - */ - protected abstract ConfigurationSource getSource(); - - /** - * Returns this {@code Configuration}. - * - * @return this {@code Configuration} - */ - protected abstract C getThis(); - - /** - * Hook that is executed right before this {@code Configuration} is saved. - *

- * The default implementation of this method does nothing. - */ - protected void preSave() {} - - /** - * Hook that is executed right after this {@code Configuration} has - * successfully been loaded. - *

- * The default implementation of this method does nothing. - */ - protected void postLoad() {} - - Properties getProperties() { - return props; - } - - /** - * Instances of a {@code Properties} class are used to configure different - * aspects of a configuration. - */ - protected static class Properties { - private final FieldNameFormatter formatter; - private final FieldFilter filter; - - /** - * Constructs a new {@code Properties} object. - * - * @param builder {@code Builder} used for construction - * @throws NullPointerException if {@code builder} is null - */ - protected Properties(Builder builder) { - this.formatter = builder.formatter; - this.filter = builder.filter; - } - - static Builder builder() { - return new Builder() { - @Override - protected Builder getThis() { - return this; - } - }; - } - - /** - * Returns the {@code FieldNameFormatter} of a configuration. - * - * @return {@code FieldNameFormatter} of a configuration - */ - public final FieldNameFormatter getFormatter() { - return formatter; - } - - /** - * Returns the {@code FieldFilter} of a configuration - * - * @return {@code FieldFilter} of a configuration - */ - public final FieldFilter getFilter() { - return filter; - } - - /** - * Builder classes are used for constructing {@code Properties}. - * - * @param type of the builder - */ - protected static abstract class Builder> { - private FieldNameFormatter formatter = FieldNameFormatters.IDENTITY; - private FieldFilter filter = FieldFilters.DEFAULT; - - protected Builder() {} - - /** - * Returns this {@code Builder}. - * - * @return this {@code Builder} - */ - protected abstract B getThis(); - - /** - * Sets the {@link FieldNameFormatter} for a configuration. - * - * @param formatter formatter for configuration - * @return this {@code Builder} - * @throws NullPointerException if {@code formatter} is null - */ - public final B setFormatter(FieldNameFormatter formatter) { - this.formatter = Objects.requireNonNull(formatter); - return getThis(); - } - - /** - * Composes the given {@link FieldFilter} with the - * {@code FieldFilters.DEFAULT} instance and any other - * previously added filters. - *

- * The added filter is not evaluated for a field if the field has - * already been filtered or by some other {@code FieldFilter}. - * - * @param filter field filter that is added - * @return this {@code Builder} - * @throws NullPointerException if {@code filter} is null - */ - public final B addFilter(FieldFilter filter) { - this.filter = this.filter.and(filter); - return getThis(); - } - - /** - * Builds a new {@code Properties} instance using the values set. - * - * @return new {@code Properties} instance - */ - public Properties build() { - return new Properties(this); - } - } - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationSource.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationSource.java deleted file mode 100644 index 863a3d7..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationSource.java +++ /dev/null @@ -1,32 +0,0 @@ -package de.exlll.configlib; - -import java.io.IOException; -import java.util.Map; - -/** - * Implementations of this class save and load {@code Map} maps that - * represent converted configurations. - * - * @param type of the configuration - */ -public interface ConfigurationSource> { - /** - * Saves the given map. - * - * @param config the configuration that the {@code map} object represents - * @param map map that is saved - * @throws IOException if an I/O error occurs when saving the {@code map} - */ - void saveConfiguration(C config, Map map) - throws IOException; - - /** - * Loads the map representing the given {@code Configuration}. - * - * @param config the configuration instance that requested the load - * @return map representing the given {@code Configuration} - * @throws IOException if an I/O error occurs when loading the map - */ - Map loadConfiguration(C config) - throws IOException; -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationStoreException.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationStoreException.java deleted file mode 100644 index f654f06..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationStoreException.java +++ /dev/null @@ -1,10 +0,0 @@ -package de.exlll.configlib; - -/** - * Signals that an error occurred while storing or loading a configuration. - */ -public final class ConfigurationStoreException extends RuntimeException { - public ConfigurationStoreException(Throwable cause) { - super(cause); - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Converter.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Converter.java deleted file mode 100644 index e6fa70f..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Converter.java +++ /dev/null @@ -1,231 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.FieldMapper.MappingInfo; -import de.exlll.configlib.annotation.ElementType; - -import java.lang.reflect.Field; - -/** - * Implementations of this interface convert field values to objects that can be - * stored by a {@link ConfigurationSource}, and vice versa. - *

- * Implementations must have a no-args constructor. - * - * @param the source type - * @param the target type - */ -public interface Converter { - /** - * Converts a field value to an object that can be stored by a - * {@code ConfigurationSource}. - *

- * If this method returns null, a {@code ConfigurationException} is thrown. - * - * @param element field value that is converted - * @param info information about the current conversion step - * @return converted field value - */ - T convertTo(S element, ConversionInfo info); - - /** - * Executes some action before the field value is converted. - * - * @param info information about the current conversion step - */ - default void preConvertTo(ConversionInfo info) {} - - /** - * Converts a converted field value back to its original representation. - *

- * If this method returns null, the default value assigned to the field will - * be kept. - * - * @param element object that should be converted back - * @param info information about the current conversion step - * @return the element's original representation - */ - S convertFrom(T element, ConversionInfo info); - - /** - * Executes some action before the converted field value is converted back - * to its original representation. - * - * @param info information about the current conversion step - */ - default void preConvertFrom(ConversionInfo info) {} - - /** - * Instances of this class contain information about the currently converted - * configuration, configuration element, and the conversion step. - */ - final class ConversionInfo { - private final MappingInfo mappingInfo; - private final Field field; - private final Object instance; - private final Object value; - private final Object mapValue; - private final Class fieldType; - private final Class valueType; - private final Class elementType; - private final String fieldName; - private final Configuration.Properties props; - private final int nestingLevel; - private int currentNestingLevel; - - private ConversionInfo( - Field field, Object instance, Object mapValue, - MappingInfo mappingInfo - ) { - this.mappingInfo = mappingInfo; - this.field = field; - this.instance = instance; - this.value = Reflect.getValue(field, instance); - this.mapValue = mapValue; - this.fieldType = field.getType(); - this.valueType = value.getClass(); - this.fieldName = field.getName(); - this.props = mappingInfo.getProperties(); - this.elementType = elementType(field); - this.nestingLevel = nestingLevel(field); - } - - private static Class elementType(Field field) { - if (field.isAnnotationPresent(ElementType.class)) { - ElementType et = field.getAnnotation(ElementType.class); - return et.value(); - } - return null; - } - - private static int nestingLevel(Field field) { - if (field.isAnnotationPresent(ElementType.class)) { - ElementType et = field.getAnnotation(ElementType.class); - return et.nestingLevel(); - } - return -1; - } - - static ConversionInfo from( - Field field, Object instance, MappingInfo mappingInfo - ) { - return new ConversionInfo(field, instance, null, mappingInfo); - } - - static ConversionInfo from( - Field field, Object instance, Object mapValue, - MappingInfo mappingInfo - ) { - return new ConversionInfo(field, instance, mapValue, mappingInfo); - } - - /** - * Returns the field all other values belong to. - * - * @return current field - */ - public Field getField() { - return field; - } - - /** - * Returns the field name. - * - * @return current field name - */ - public String getFieldName() { - return fieldName; - } - - /** - * Returns the object the field belongs to, i.e. the instance currently - * converted. - * - * @return object the field belongs to - */ - public Object getInstance() { - return instance; - } - - /** - * Returns the default value assigned to that field. - * - * @return default value assigned to field - */ - public Object getValue() { - return value; - } - - /** - * When loading, returns the converted field value, otherwise returns null. - * - * @return converted field value or null - */ - public Object getMapValue() { - return mapValue; - } - - /** - * Returns the type of the field. - * - * @return field type - */ - public Class getFieldType() { - return fieldType; - } - - /** - * Returns the type of the default value assigned to the field. - * - * @return type default value assigned to field - */ - public Class getValueType() { - return valueType; - } - - /** - * Returns the {@code Configuration.Properties} instance of the currently - * converted configuration. - * - * @return properties of currently converted configuration - */ - public Configuration.Properties getProperties() { - return props; - } - - /** - * Returns the value of the {@code ElementType} annotation or null if the - * field is not annotated with this annotation. - * - * @return value of the {@code ElementType} annotation or null - */ - public Class getElementType() { - return elementType; - } - - /** - * Returns whether the field is annotated with the {@code ElementType} - * annotation. - * - * @return true, if field is annotated with {@code ElementType}. - */ - public boolean hasElementType() { - return elementType != null; - } - - int getNestingLevel() { - return nestingLevel; - } - - int getCurrentNestingLevel() { - return currentNestingLevel; - } - - void incCurrentNestingLevel() { - currentNestingLevel++; - } - - MappingInfo getMappingInfo() { - return this.mappingInfo; - } - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Converters.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Converters.java deleted file mode 100644 index fcb1fbc..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Converters.java +++ /dev/null @@ -1,551 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.Converter.ConversionInfo; -import de.exlll.configlib.annotation.Convert; - -import java.lang.reflect.Field; -import java.util.*; -import java.util.function.Function; - -import static de.exlll.configlib.Validator.*; -import static java.util.stream.Collectors.*; - -final class Converters { - private static final Map>, Converter> cache - = new WeakHashMap<>(); - static final IdentityConverter IDENTITY_CONVERTER - = new IdentityConverter(); - static final SimpleTypeConverter SIMPLE_TYPE_CONVERTER - = new SimpleTypeConverter(); - static final EnumConverter ENUM_CONVERTER - = new EnumConverter(); - static final ListConverter LIST_CONVERTER - = new ListConverter(); - static final SetConverter SET_CONVERTER - = new SetConverter(); - static final MapConverter MAP_CONVERTER - = new MapConverter(); - static final SimpleListConverter SIMPLE_LIST_CONVERTER - = new SimpleListConverter(); - static final SimpleSetConverter SIMPLE_SET_CONVERTER - = new SimpleSetConverter(); - static final SimpleMapConverter SIMPLE_MAP_CONVERTER - = new SimpleMapConverter(); - static final ConfigurationElementConverter ELEMENT_CONVERTER - = new ConfigurationElementConverter(); - - static Object convertTo(ConversionInfo info) { - Converter converter = selectConverter( - info.getValueType(), info - ); - converter.preConvertTo(info); - return tryConvertTo(converter, info); - } - - private static Object tryConvertTo( - Converter converter, ConversionInfo info - ) { - try { - return converter.convertTo(info.getValue(), info); - } catch (ClassCastException e) { - String msg = "Converter '" + converter.getClass().getSimpleName() + "'" + - " cannot convert value '" + info.getValue() + "' of field '" + - info.getFieldName() + "' because it expects a different type."; - throw new ConfigurationException(msg, e); - } - } - - static Object convertFrom(ConversionInfo info) { - Converter converter = selectConverter( - info.getValueType(), info - ); - converter.preConvertFrom(info); - return tryConvertFrom(converter, info); - } - - private static Object tryConvertFrom( - Converter converter, ConversionInfo info - ) { - try { - return converter.convertFrom(info.getMapValue(), info); - } catch (ClassCastException | IllegalArgumentException e) { - String msg = "The value for field '" + info.getFieldName() + "' with " + - "type '" + getClsName(info.getFieldType()) + "' cannot " + - "be converted back to its original representation because a " + - "type mismatch occurred."; - throw new ConfigurationException(msg, e); - } - } - - private static String getClsName(Class cls) { - return cls.getSimpleName(); - } - - private static Converter selectConverter( - Class valueType, ConversionInfo info - ) { - Converter converter; - if (Reflect.hasNoConvert(info.getField())) { - converter = IDENTITY_CONVERTER; - } else if (Reflect.hasConverter(info.getField())) { - converter = instantiateConverter(info.getField()); - } else if (Reflect.isSimpleType(valueType)) { - converter = SIMPLE_TYPE_CONVERTER; - } else { - converter = selectNonSimpleConverter(valueType, info); - } - return toObjectConverter(converter); - } - - private static Converter selectNonSimpleConverter( - Class valueType, ConversionInfo info - ) { - Converter converter; - if (Reflect.isEnumType(valueType) || - /* type is a string when converting back */ - (valueType == String.class)) { - converter = ENUM_CONVERTER; - } else if (Reflect.isContainerType(valueType)) { - converter = selectContainerConverter(valueType, info); - } else { - converter = ELEMENT_CONVERTER; - } - return toObjectConverter(converter); - } - - private static Converter instantiateConverter(Field field) { - Convert convert = field.getAnnotation(Convert.class); - return cache.computeIfAbsent(convert.value(), cls -> { - checkConverterHasNoArgsConstructor(cls, field.getName()); - return Reflect.newInstance(cls); - }); - } - - private static Converter selectContainerConverter( - Class valueType, ConversionInfo info - ) { - if (info.hasElementType()) { - return selectElementTypeContainerConverter(valueType); - } else { - return selectSimpleContainerConverter(valueType); - } - } - - private static Converter selectElementTypeContainerConverter( - Class valueType - ) { - return selector( - LIST_CONVERTER, SET_CONVERTER, MAP_CONVERTER - ).apply(valueType); - } - - private static Converter selectSimpleContainerConverter( - Class valueType - ) { - return selector( - SIMPLE_LIST_CONVERTER, SIMPLE_SET_CONVERTER, SIMPLE_MAP_CONVERTER - ).apply(valueType); - } - - static Function, R> selector(R listValue, R setValue, R mapValue) { - return containerClass -> { - if (List.class.isAssignableFrom(containerClass)) { - return listValue; - } else if (Set.class.isAssignableFrom(containerClass)) { - return setValue; - } else { - return mapValue; - } - }; - } - - static String selectContainerName(Class containerType) { - return selector("list", "set", "map").apply(containerType); - } - - private static Converter toObjectConverter( - Converter converter - ) { - /* This cast may result in a ClassCastException when converting objects - * back to their original representation. This happens if the type of the - * converted object has changed for some reason (e.g. by a configuration - * mistake). However, the ClassCastException is later caught and translated - * to a ConfigurationException to give additional information about what - * happened. */ - @SuppressWarnings("unchecked") - Converter c = (Converter) converter; - return c; - } - - private static final class SimpleListConverter - implements Converter, List> { - @Override - public List convertTo(List element, ConversionInfo info) { - return element; - } - - @Override - public void preConvertTo(ConversionInfo info) { - checkContainerValuesNotNull(info); - checkContainerValuesSimpleType(info); - } - - @Override - public List convertFrom(List element, ConversionInfo info) { - return element; - } - } - - private static final class SimpleSetConverter - implements Converter, Set> { - @Override - public Set convertTo(Set element, ConversionInfo info) { - return element; - } - - @Override - public void preConvertTo(ConversionInfo info) { - checkContainerValuesNotNull(info); - checkContainerValuesSimpleType(info); - } - - @Override - public Set convertFrom(Set element, ConversionInfo info) { - return element; - } - } - - private static final class SimpleMapConverter - implements Converter, Map> { - @Override - public Map convertTo(Map element, ConversionInfo info) { - return element; - } - - @Override - public void preConvertTo(ConversionInfo info) { - checkMapKeysAndValues(info); - checkContainerValuesSimpleType(info); - } - - @Override - public Map convertFrom(Map element, ConversionInfo info) { - return element; - } - } - - private static final class ListConverter - implements Converter, List> { - @Override - public List convertTo(List element, ConversionInfo info) { - if (element.isEmpty()) { - return element; - } - Object o = element.get(0); - Function f = createToConversionFunction(o, info); - return element.stream().map(f).collect(toList()); - } - - @Override - public void preConvertTo(ConversionInfo info) { - checkElementType(info); - checkContainerValuesNotNull(info); - checkContainerTypes(info); - } - - @Override - public List convertFrom(List element, ConversionInfo info) { - if (element.isEmpty()) { - return element; - } - Object o = element.get(0); - Function f = createFromConversionFunction(o, info); - return element.stream().map(f).collect(toList()); - } - - @Override - public void preConvertFrom(ConversionInfo info) { - checkElementType(info); - } - } - - private static final class SetConverter - implements Converter, Set> { - @Override - public Set convertTo(Set element, ConversionInfo info) { - if (element.isEmpty()) { - return element; - } - Object o = element.iterator().next(); - Function f = createToConversionFunction(o, info); - return element.stream().map(f).collect(toSet()); - } - - @Override - public void preConvertTo(ConversionInfo info) { - checkElementType(info); - checkContainerValuesNotNull(info); - checkContainerTypes(info); - } - - @Override - public Set convertFrom(Set element, ConversionInfo info) { - if (element.isEmpty()) { - return element; - } - Object o = element.iterator().next(); - Function f = createFromConversionFunction(o, info); - return element.stream().map(f).collect(toSet()); - } - - @Override - public void preConvertFrom(ConversionInfo info) { - checkElementType(info); - } - } - - private static final class MapConverter - implements Converter, Map> { - @Override - public Map convertTo(Map element, ConversionInfo info) { - if (element.isEmpty()) { - return element; - } - Object o = element.values().iterator().next(); - Function cf = createToConversionFunction(o, info); - Function, ?> f = e -> cf.apply(e.getValue()); - return element.entrySet().stream().collect(toMap(Map.Entry::getKey, f)); - } - - @Override - public void preConvertTo(ConversionInfo info) { - checkElementType(info); - checkMapKeysAndValues(info); - checkContainerTypes(info); - } - - @Override - public Map convertFrom(Map element, ConversionInfo info) { - if (element.isEmpty()) { - return element; - } - Object o = element.values().iterator().next(); - Function cf = createFromConversionFunction(o, info); - Function, ?> f = e -> cf.apply(e.getValue()); - return element.entrySet().stream().collect(toMap(Map.Entry::getKey, f)); - } - - @Override - public void preConvertFrom(ConversionInfo info) { - checkElementType(info); - } - } - - private static Function createToConversionFunction( - Object element, ConversionInfo info - ) { - checkNestingLevel(element, info); - if (Reflect.isContainerType(element.getClass())) { - info.incCurrentNestingLevel(); - } - Converter converter = selectNonSimpleConverter( - element.getClass(), info - ); - return o -> converter.convertTo(o, info); - } - - private static Function createFromConversionFunction( - Object element, ConversionInfo info - ) { - boolean currentLevelSameAsExpected = - info.getNestingLevel() == info.getCurrentNestingLevel(); - checkCurrentLevelSameAsExpectedRequiresMapOrString( - currentLevelSameAsExpected, element, info - ); - if ((element instanceof Map) && currentLevelSameAsExpected) { - return o -> { - Map map = toTypeMap(o, null); - Object inst = Reflect.newInstance(info.getElementType()); - FieldMapper.instanceFromMap(inst, map, info.getMappingInfo()); - return inst; - }; - } else if ((element instanceof String) && currentLevelSameAsExpected) { - return createNonSimpleConverter(element, info); - } else { - info.incCurrentNestingLevel(); - return createNonSimpleConverter(element, info); - } - } - - private static Function createNonSimpleConverter( - Object element, ConversionInfo info - ) { - Converter converter = selectNonSimpleConverter( - element.getClass(), info - ); - return o -> converter.convertFrom(o, info); - } - - private static Map toTypeMap(Object value, String fn) { - checkIsMap(value, fn); - checkMapKeysAreStrings((Map) value, fn); - - // The following cast won't fail because we just verified that - // it's a Map. - @SuppressWarnings("unchecked") - Map map = (Map) value; - - return map; - } - - private static final class IdentityConverter - implements Converter { - - @Override - public Object convertTo(Object element, ConversionInfo info) { - return element; - } - - @Override - public Object convertFrom(Object element, ConversionInfo info) { - return element; - } - } - - private static final class SimpleTypeConverter - implements Converter { - - @Override - public Object convertTo(Object element, ConversionInfo info) { - return element; - } - - @Override - public Object convertFrom(Object element, ConversionInfo info) { - if (info.getFieldType() == element.getClass()) { - return element; - } - if (element instanceof Number) { - return convertNumber(info.getFieldType(), (Number) element); - } - if (element instanceof String) { - return convertString((String) element); - } - return element; - } - - private Object convertNumber(Class target, Number value) { - if (target == byte.class || target == Byte.class) { - return value.byteValue(); - } else if (target == short.class || target == Short.class) { - return value.shortValue(); - } else if (target == int.class || target == Integer.class) { - return value.intValue(); - } else if (target == long.class || target == Long.class) { - return value.longValue(); - } else if (target == float.class || target == Float.class) { - return value.floatValue(); - } else if (target == double.class || target == Double.class) { - return value.doubleValue(); - } else { - String msg = "Number '" + value + "' cannot be converted " + - "to type '" + target + "'"; - throw new IllegalArgumentException(msg); - } - } - - private Object convertString(String s) { - int length = s.length(); - if (length == 0) { - String msg = "An empty string cannot be converted to a character."; - throw new IllegalArgumentException(msg); - } - if (length > 1) { - String msg = "String '" + s + "' is too long to " + - "be converted to a character"; - throw new IllegalArgumentException(msg); - } - return s.charAt(0); - } - } - - private static final class EnumConverter - implements Converter, String> { - - @Override - public String convertTo(Enum element, ConversionInfo info) { - return element.toString(); - } - - @Override - public void preConvertFrom(ConversionInfo info) { - checkEnumValueIsString(info); - } - - @Override - public Enum convertFrom(String element, ConversionInfo info) { - Class cls = getEnumClass(info); - try { - /* cast won't fail because we know that it's an enum */ - @SuppressWarnings("unchecked") - Enum enm = Enum.valueOf(cls, element); - return enm; - } catch (IllegalArgumentException e) { - checkElementTypeIsEnumType(cls, info); - String in = selectWord(info); - String msg = "Cannot initialize " + in + " because there is no " + - "enum constant '" + element + "'.\nValid constants are: " + - Arrays.toString(cls.getEnumConstants()); - throw new IllegalArgumentException(msg, e); - } - } - - private String selectWord(ConversionInfo info) { - String fn = info.getFieldName(); - if (Reflect.isContainerType(info.getFieldType())) { - String w = selectContainerName(info.getValueType()); - return "an enum element of " + w + " '" + fn + "'"; - } - return "enum '" + fn + "' "; - } - - @SuppressWarnings("unchecked") - private Class getEnumClass(ConversionInfo info) { - /* this cast won't fail because this method is only called by a - * Converter that converts enum types. */ - return (Class) (!info.hasElementType() - ? info.getValue().getClass() - : info.getElementType()); - } - } - - private static final class ConfigurationElementConverter - implements Converter { - - @Override - public Object convertTo(Object element, ConversionInfo info) { - return FieldMapper.instanceToMap(element, info.getMappingInfo()); - } - - @Override - public void preConvertTo(ConversionInfo info) { - checkTypeIsConfigurationElement(info.getValueType(), info.getFieldName()); - checkTypeHasNoArgsConstructor(info); - } - - @Override - public Object convertFrom(Object element, ConversionInfo info) { - checkElementIsConvertibleToConfigurationElement(element, info); - Object newInstance = Reflect.newInstance(info.getValueType()); - Map typeMap = toTypeMap(element, info.getFieldName()); - FieldMapper.instanceFromMap(newInstance, typeMap, info.getMappingInfo()); - return newInstance; - } - - @Override - public void preConvertFrom(ConversionInfo info) { - checkTypeHasNoArgsConstructor(info); - checkTypeIsConfigurationElement(info.getValueType(), info.getFieldName()); - } - } -} \ No newline at end of file diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/FieldMapper.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/FieldMapper.java deleted file mode 100644 index 727be3f..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/FieldMapper.java +++ /dev/null @@ -1,120 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.Converter.ConversionInfo; -import de.exlll.configlib.annotation.Format; -import de.exlll.configlib.filter.FieldFilter; -import de.exlll.configlib.format.FieldNameFormatter; - -import java.lang.reflect.Field; -import java.util.LinkedHashMap; -import java.util.Map; - -import static de.exlll.configlib.Validator.*; - -enum FieldMapper { - ; - - static Map instanceToMap(Object inst, MappingInfo mappingInfo) { - Map map = new LinkedHashMap<>(); - Configuration.Properties props = mappingInfo.getProperties(); - FieldFilter filter = props.getFilter(); - for (Field field : filter.filterDeclaredFieldsOf(inst.getClass())) { - Object val = toConvertibleObject(field, inst, mappingInfo); - FieldNameFormatter fnf = selectFormatter(mappingInfo); - String fn = fnf.fromFieldName(field.getName()); - map.put(fn, val); - } - return map; - } - - private static Object toConvertibleObject( - Field field, Object instance, MappingInfo mappingInfo - ) { - checkDefaultValueNull(field, instance); - ConversionInfo info = ConversionInfo.from(field, instance, mappingInfo); - checkFieldWithElementTypeIsContainer(info); - Object converted = Converters.convertTo(info); - checkConverterNotReturnsNull(converted, info); - return converted; - } - - static void instanceFromMap( - Object inst, Map instMap, MappingInfo mappingInfo - ) { - FieldFilter filter = mappingInfo.getProperties().getFilter(); - for (Field field : filter.filterDeclaredFieldsOf(inst.getClass())) { - FieldNameFormatter fnf = selectFormatter(mappingInfo); - String fn = fnf.fromFieldName(field.getName()); - Object mapValue = instMap.get(fn); - if (mapValue != null) { - fromConvertedObject(field, inst, mapValue, mappingInfo); - } - } - } - - private static void fromConvertedObject( - Field field, Object instance, Object mapValue, - MappingInfo mappingInfo - ) { - checkDefaultValueNull(field, instance); - ConversionInfo info = ConversionInfo.from( - field, instance, mapValue, mappingInfo - ); - checkFieldWithElementTypeIsContainer(info); - Object convert = Converters.convertFrom(info); - - if (convert == null) { - return; - } - - if (Reflect.isContainerType(info.getFieldType())) { - checkFieldTypeAssignableFrom(convert.getClass(), info); - } - - Reflect.setValue(field, instance, convert); - } - - private static void checkDefaultValueNull(Field field, Object instance) { - Object val = Reflect.getValue(field, instance); - checkNotNull(val, field.getName()); - } - - static FieldNameFormatter selectFormatter(MappingInfo info) { - Configuration configuration = info.getConfiguration(); - Configuration.Properties props = info.getProperties(); - if ((configuration != null) && - Reflect.hasFormatter(configuration.getClass())) { - Format format = configuration.getClass() - .getAnnotation(Format.class); - return (format.formatterClass() != FieldNameFormatter.class) - ? Reflect.newInstance(format.formatterClass()) - : format.value(); - } - return props.getFormatter(); - } - - static final class MappingInfo { - private final Configuration configuration; - private final Configuration.Properties properties; - - MappingInfo( - Configuration configuration, - Configuration.Properties properties - ) { - this.configuration = configuration; - this.properties = properties; - } - - Configuration getConfiguration() { - return configuration; - } - - Configuration.Properties getProperties() { - return properties; - } - - static MappingInfo from(Configuration configuration) { - return new MappingInfo(configuration, configuration.getProperties()); - } - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Reflect.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Reflect.java deleted file mode 100644 index 659f814..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Reflect.java +++ /dev/null @@ -1,110 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.annotation.ConfigurationElement; -import de.exlll.configlib.annotation.Convert; -import de.exlll.configlib.annotation.Format; -import de.exlll.configlib.annotation.NoConvert; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.util.*; - -enum Reflect { - ; - private static final Set> SIMPLE_TYPES = new HashSet<>(Arrays.asList( - Boolean.class, - Byte.class, - Character.class, - Short.class, - Integer.class, - Long.class, - Float.class, - Double.class, - String.class - )); - - static boolean isSimpleType(Class cls) { - return cls.isPrimitive() || SIMPLE_TYPES.contains(cls); - } - - static boolean isContainerType(Class cls) { - return List.class.isAssignableFrom(cls) || - Set.class.isAssignableFrom(cls) || - Map.class.isAssignableFrom(cls); - } - - static boolean isEnumType(Class cls) { - return cls.isEnum(); - } - - static T newInstance(Class cls) { - try { - Constructor constructor = cls.getDeclaredConstructor(); - constructor.setAccessible(true); - return constructor.newInstance(); - } catch (NoSuchMethodException e) { - String msg = "Class " + cls.getSimpleName() + " doesn't have a " + - "no-args constructor."; - throw new ConfigurationException(msg, e); - } catch (IllegalAccessException e) { - /* This exception should not be thrown because - * we set the field to be accessible. */ - String msg = "No-args constructor of class " + cls.getSimpleName() + - " not accessible."; - throw new ConfigurationException(msg, e); - } catch (InstantiationException e) { - String msg = "Class " + cls.getSimpleName() + " not instantiable."; - throw new ConfigurationException(msg, e); - } catch (InvocationTargetException e) { - String msg = "Constructor of class " + cls.getSimpleName() + - " has thrown an exception."; - throw new ConfigurationException(msg, e); - } - } - - static Object getValue(Field field, Object inst) { - try { - field.setAccessible(true); - return field.get(inst); - } catch (IllegalAccessException e) { - /* This exception should not be thrown because - * we set the field to be accessible. */ - String msg = "Illegal access of field '" + field + "' " + - "on object " + inst + "."; - throw new ConfigurationException(msg, e); - } - } - - static void setValue(Field field, Object inst, Object value) { - try { - field.setAccessible(true); - field.set(inst, value); - } catch (IllegalAccessException e) { - String msg = "Illegal access of field '" + field + "' " + - "on object " + inst + "."; - throw new ConfigurationException(msg, e); - } - } - - static boolean hasConverter(Field field) { - return field.isAnnotationPresent(Convert.class); - } - - static boolean hasNoConvert(Field field) { - return field.isAnnotationPresent(NoConvert.class); - } - - static boolean hasFormatter(Class cls) { - return cls.isAnnotationPresent(Format.class); - } - - static boolean isConfigurationElement(Class cls) { - return cls.isAnnotationPresent(ConfigurationElement.class); - } - - static boolean hasNoArgConstructor(Class cls) { - return Arrays.stream(cls.getDeclaredConstructors()) - .anyMatch(c -> c.getParameterCount() == 0); - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/Validator.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/Validator.java deleted file mode 100644 index 67a0117..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/Validator.java +++ /dev/null @@ -1,357 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.Converter.ConversionInfo; - -import java.lang.reflect.Modifier; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -final class Validator { - - static void checkNotNull(Object o, String fn) { - if (o == null) { - String msg = "The value of field '" + fn + "' is null.\n" + - "Please assign a non-null default value or remove this field."; - throw new ConfigurationException(msg); - } - } - - static void checkContainerTypes(ConversionInfo info) { - Object value = info.getValue(); - Collection collection = toCollection(value); - checkCollectionTypes(collection, info); - } - - private static void checkCollectionTypes( - Collection collection, ConversionInfo info - ) { - for (Object element : collection) { - if (Reflect.isContainerType(element.getClass())) { - Collection container = toCollection(element); - checkCollectionTypes(container, info); - } else { - checkCollectionType(element, info); - } - } - } - - private static void checkCollectionType(Object element, ConversionInfo info) { - Class cls = element.getClass(); - if (cls != info.getElementType()) { - String cNameField = selectContainerNameField(info); - String cValues = selectContainerValues(info); - String msg = "The type of " + cNameField + " doesn't match the " + - "type indicated by the ElementType annotation.\n" + - "Required type: '" + getClsName(info.getElementType()) + - "'\tActual type: '" + getClsName(cls) + - "'\n" + cValues; - throw new ConfigurationException(msg); - } - } - - private static String selectContainerValues(ConversionInfo info) { - Object value = info.getValue(); - return Converters.selector( - "All elements: " + value, - "All elements: " + value, - "All entries: " + value - ).apply(info.getValueType()); - } - - private static String selectContainerNameField(ConversionInfo info) { - String fieldName = info.getFieldName(); - return Converters.selector( - "an element of list '" + fieldName + "'", - "an element of set '" + fieldName + "'", - "a value of map '" + fieldName + "'" - ).apply(info.getValueType()); - } - - private static Collection toCollection(Object container) { - if (container instanceof List || container instanceof Set) { - return (Collection) container; - } else { - Map map = (Map) container; - return map.values(); - } - } - - static void checkMapKeysAndValues(ConversionInfo info) { - checkMapKeysSimple((Map) info.getValue(), info.getFieldName()); - checkContainerValuesNotNull(info); - } - - private static void checkMapKeysSimple(Map map, String fn) { - for (Object o : map.keySet()) { - if (!Reflect.isSimpleType(o.getClass())) { - String msg = "The keys of map '" + fn + "' must be simple types."; - throw new ConfigurationException(msg); - } - } - } - - static void checkContainerValuesNotNull(ConversionInfo info) { - Collection collection = toCollection(info.getValue()); - checkCollectionValuesNotNull(collection, info); - } - - private static void checkCollectionValuesNotNull( - Collection col, ConversionInfo info - ) { - for (Object element : col) { - checkCollectionValueNotNull(element, info); - if (Reflect.isContainerType(element.getClass())) { - Collection container = toCollection(element); - checkCollectionValuesNotNull(container, info); - } - } - } - - private static void checkCollectionValueNotNull( - Object element, ConversionInfo info - ) { - if (element == null) { - String cnf = selectContainerNameField(info) - .replaceFirst("a", "A"); - String msg = cnf + " is null.\n" + - "Please either remove or replace this element." + - "\n" + selectContainerValues(info); - throw new ConfigurationException(msg); - } - } - - static void checkContainerValuesSimpleType(ConversionInfo info) { - Collection collection = toCollection(info.getValue()); - checkCollectionValuesSimpleType(collection, info); - } - - private static void checkCollectionValuesSimpleType( - Collection collection, ConversionInfo info - ) { - for (Object element : collection) { - if (Reflect.isContainerType(element.getClass())) { - Collection elements = toCollection(element); - checkCollectionValuesSimpleType(elements, info); - } else { - checkCollectionValueSimpleType(element, info); - } - } - } - - private static void checkCollectionValueSimpleType( - Object element, ConversionInfo info - ) { - if (!Reflect.isSimpleType(element.getClass())) { - String cn = Converters.selectContainerName(info.getValueType()); - String cnf = selectContainerNameField(info); - String fieldName = info.getFieldName(); - String msg = "The type of " + cnf + " is not a simple type but " + cn + - " '" + fieldName + "' is missing the ElementType annotation." + - "\n" + selectContainerValues(info); - throw new ConfigurationException(msg); - } - } - - static void checkTypeIsConfigurationElement(Class cls, String fn) { - if (!Reflect.isConfigurationElement(cls)) { - String msg = "Type '" + getClsName(cls) + "' of field '" + - fn + "' is not annotated as a configuration element."; - throw new ConfigurationException(msg); - } - } - - private static String getClsName(Class cls) { - String clsName = cls.getSimpleName(); - if (clsName.equals("")) { - clsName = cls.getName(); - } - return clsName; - } - - static void checkIsMap(Object value, String fn) { - Class cls = value.getClass(); - if (!Map.class.isAssignableFrom(cls)) { - String msg = "Initializing field '" + fn + "' requires a " + - "Map but the given object is not a map.\n" + - "Type: '" + cls.getSimpleName() + "'\tValue: '" + value + "'"; - throw new ConfigurationException(msg); - } - } - - static void checkMapKeysAreStrings(Map map, String fn) { - for (Map.Entry entry : map.entrySet()) { - Object key = entry.getKey(); - if ((key == null) || (key.getClass() != String.class)) { - String msg = "Initializing field '" + fn + "' requires a " + - "Map but the given map contains " + - "non-string keys.\nAll entries: " + map; - throw new ConfigurationException(msg); - } - } - } - - static void checkElementType(ConversionInfo info) { - Class elementType = info.getElementType(); - if (!elementType.isEnum()) - checkElementTypeIsConfigurationElement(info); - checkElementTypeIsConcrete(info); - if (!elementType.isEnum()) - checkElementTypeHasNoArgsConstructor(info); - } - - static void checkFieldWithElementTypeIsContainer(ConversionInfo info) { - boolean isContainer = Reflect.isContainerType(info.getValueType()); - if (info.hasElementType() && !isContainer) { - String msg = "Field '" + info.getFieldName() + "' is annotated with " + - "the ElementType annotation but is not a List, Set or Map."; - throw new ConfigurationException(msg); - } - } - - private static void checkElementTypeIsConfigurationElement(ConversionInfo info) { - Class elementType = info.getElementType(); - if (!Reflect.isConfigurationElement(elementType)) { - String msg = "The element type '" + getClsName(elementType) + "'" + - " of field '" + info.getFieldName() + "' is not a " + - "configuration element."; - throw new ConfigurationException(msg); - } - } - - private static void checkElementTypeIsConcrete(ConversionInfo info) { - Class elementType = info.getElementType(); - - String msg = getType(elementType); - if (msg != null) { - msg = "The element type of field '" + info.getFieldName() + "' must " + - "be a concrete class but type '" + - getClsName(elementType) + "' is " + msg; - throw new ConfigurationException(msg); - } - } - - private static String getType(Class cls) { - String msg = null; - - if (cls.isInterface()) { - msg = "an interface."; - } else if (cls.isPrimitive()) { - msg = "primitive."; - } else if (cls.isArray()) { - msg = "an array."; - } else if (Modifier.isAbstract(cls.getModifiers())) { - msg = "an abstract class."; - } - return msg; - } - - private static void checkElementTypeHasNoArgsConstructor(ConversionInfo info) { - Class elementType = info.getElementType(); - if (!Reflect.hasNoArgConstructor(elementType)) { - String msg = "The element type '" + elementType.getSimpleName() + "'" + - " of field '" + info.getFieldName() + "' doesn't have " + - "a no-args constructor."; - throw new ConfigurationException(msg); - } - } - - static void checkTypeHasNoArgsConstructor(ConversionInfo info) { - Class valueType = info.getValueType(); - if (!Reflect.hasNoArgConstructor(valueType)) { - String msg = "Type '" + getClsName(valueType) + "' of field '" + - info.getFieldName() + "' doesn't have a no-args constructor."; - throw new ConfigurationException(msg); - } - } - - static void checkConverterHasNoArgsConstructor(Class converterClass, String fn) { - if (!Reflect.hasNoArgConstructor(converterClass)) { - String msg = "Converter '" + converterClass.getSimpleName() + "' used " + - "on field '" + fn + "' doesn't have a no-args constructor."; - throw new ConfigurationException(msg); - } - } - - static void checkEnumValueIsString(ConversionInfo info) { - Object val = info.getMapValue(); - if (!(val instanceof String)) { - String sn = val.getClass().getSimpleName(); - String msg = "Initializing enum '" + info.getFieldName() + "' " + - "requires a string but '" + val + "' is of type '" + sn + "'."; - throw new ConfigurationException(msg); - } - } - - static void checkFieldTypeAssignableFrom(Class type, ConversionInfo info) { - Class fieldType = info.getFieldType(); - if (!fieldType.isAssignableFrom(type)) { - String msg = "Can not set field '" + info.getFieldName() + "' with " + - "type '" + getClsName(fieldType) + "' to '" + - getClsName(type) + "'."; - throw new ConfigurationException(msg); - } - } - - static void checkElementIsConvertibleToConfigurationElement( - Object element, ConversionInfo info - ) { - Class eClass = element.getClass(); - if (Reflect.isContainerType(info.getFieldType()) && - !Map.class.isAssignableFrom(eClass)) { - String msg = "Initializing field '" + info.getFieldName() + "' " + - "requires objects of type Map but element " + - "'" + element + "' is of type '" + getClsName(eClass) + "'."; - throw new IllegalArgumentException(msg); - } - } - - static void checkNestingLevel(Object element, ConversionInfo info) { - if (!Reflect.isContainerType(element.getClass())) { - if (info.getNestingLevel() != info.getCurrentNestingLevel()) { - String msg = "Field '" + info.getFieldName() + "' of class " + - "'" + getClsName(info.getInstance().getClass()) + "' " + - "has a nesting level of " + info.getNestingLevel() + - " but the first object of type '" + - getClsName(info.getElementType()) + "' was found on " + - "level " + info.getCurrentNestingLevel() + "."; - throw new ConfigurationException(msg); - } - } - } - - static void checkCurrentLevelSameAsExpectedRequiresMapOrString( - boolean currentLevelSameAsExpected, - Object element, ConversionInfo info - ) { - boolean isMapOrString = (element instanceof Map) || - (element instanceof String); - if (currentLevelSameAsExpected && !isMapOrString) { - Class cls = info.getInstance().getClass(); - String msg = "Field '" + info.getFieldName() + "' of class '" + - getClsName(cls) + "' has a nesting level" + - " of " + info.getNestingLevel() + " but element '" + element + - "' of type '" + getClsName(element.getClass()) + "' cannot be " + - "converted to '" + getClsName(info.getElementType()) + "'."; - throw new ConfigurationException(msg); - } - } - - static void checkElementTypeIsEnumType(Class type, ConversionInfo info) { - if (!Reflect.isEnumType(type)) { - String msg = "Element type '" + getClsName(type) + "' of field " + - "'" + info.getFieldName() + "' is not an enum type."; - throw new IllegalArgumentException(msg); - } - } - - static void checkConverterNotReturnsNull(Object converted, ConversionInfo info) { - if (converted == null) { - String msg = "Failed to convert value '" + info.getValue() + "' of " + - "field '" + info.getFieldName() + "' because the converter " + - "returned null."; - throw new ConfigurationException(msg); - } - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Comment.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Comment.java deleted file mode 100644 index d418f76..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Comment.java +++ /dev/null @@ -1,32 +0,0 @@ -package de.exlll.configlib.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Indicates that the annotated element is saved together with explanatory - * comments describing it. - *

- * For {@link de.exlll.configlib.configs.yaml.YamlConfiguration YamlConfiguration}s: - *

    - *
  • - * If this annotation is used on a class, the comments returned by the - * {@link #value()} method are saved at the beginning of the configuration file. - *
  • - *
  • - * If this annotation is used on a field, the comments are saved above the field name. - *
  • - *
- */ -@Target({ElementType.FIELD, ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface Comment { - /** - * Returns the comments of the annotated type or field. - * - * @return class or field comments - */ - String[] value(); -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ConfigurationElement.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ConfigurationElement.java deleted file mode 100644 index 560d33c..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ConfigurationElement.java +++ /dev/null @@ -1,15 +0,0 @@ -package de.exlll.configlib.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Enables conversion of instances of the annotated type. - *

- * {@code ConfigurationElement}s must have a no-args constructor. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface ConfigurationElement {} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Convert.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Convert.java deleted file mode 100644 index 814843f..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Convert.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.exlll.configlib.annotation; - -import de.exlll.configlib.Converter; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Indicates that a custom conversion mechanism is used to convert the - * annotated field. - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface Convert { - Class> value(); -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ElementType.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ElementType.java deleted file mode 100644 index 92bf1f6..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/ElementType.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.exlll.configlib.annotation; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Indicates the type of elements a {@code Collection} or {@code Map} contains. - *

- * This annotation must only be used if a {@code Collection} or {@code Map} contains - * elements whose type is not simple. Note that {@code Map} keys can only be of some - * simple type. - *

- * If collections are nested, the {@code nestingLevel} must be set. Examples: - *

    - *
  • nestingLevel 1: {@code List>}
  • - *
  • nestingLevel 1: {@code List>}
  • - *
  • nestingLevel 1: {@code List>}
  • - *
  • nestingLevel 2: {@code List>>}
  • - *
  • nestingLevel 2: {@code List>>}
  • - *
  • nestingLevel 2: {@code List>>}
  • - *
- */ -@Target(java.lang.annotation.ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface ElementType { - /** - * Returns the type of elements a {@code Collection} or {@code Map} contains. - * - * @return type of elements. - */ - Class value(); - - /** - * Returns the nesting level - * - * @return nesting level - */ - int nestingLevel() default 0; -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Format.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Format.java deleted file mode 100644 index 749eaba..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/Format.java +++ /dev/null @@ -1,38 +0,0 @@ -package de.exlll.configlib.annotation; - -import de.exlll.configlib.format.FieldNameFormatter; -import de.exlll.configlib.format.FieldNameFormatters; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Indicates that a specific {@code FieldNameFormatter} is used. If a - * {@link #formatterClass()} is specified, the {@code FieldNameFormatters} - * returned by {@link #value()} is ignored. The {@code formatterClass} must - * be instantiable and must have a no-args constructor. - *

- * This annotation takes precedence over the value set in properties object. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface Format { - /** - * Returns the {@code FieldNameFormatters} instance to be for formatting. - * The return value of this method is ignored if a {@code formatterClass()} - * is set. - * - * @return {@code FieldNameFormatters} instance - */ - FieldNameFormatters value() default FieldNameFormatters.IDENTITY; - - /** - * Returns the class of a {@code FieldNameFormatter} implementation. - * The class must be instantiable and must have a no-args constructor. - * - * @return class of {@code FieldNameFormatter} implementation - */ - Class formatterClass() default FieldNameFormatter.class; -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/NoConvert.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/NoConvert.java deleted file mode 100644 index 78322db..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/annotation/NoConvert.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.exlll.configlib.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * Indicates that the annotated field should not be converted but instead used as is. - *

- * This may be useful if the configuration knows how to (de-)serialize - * instances of that type. For example, a {@code BukkitYamlConfiguration} - * knows how to serialize {@code ItemStack} instances. - */ -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface NoConvert {} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlComments.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlComments.java deleted file mode 100644 index b0a51ee..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlComments.java +++ /dev/null @@ -1,76 +0,0 @@ -package de.exlll.configlib.configs.yaml; - -import de.exlll.configlib.Comments; -import de.exlll.configlib.format.FieldNameFormatter; - -import java.util.List; -import java.util.Map; - -import static java.util.stream.Collectors.joining; -import static java.util.stream.Collectors.toMap; - -final class YamlComments { - private final Comments comments; - - YamlComments(Comments comments) { - this.comments = comments; - } - - String classCommentsAsString() { - List classComments = comments.getClassComments(); - return commentListToString(classComments); - } - - Map fieldCommentAsStrings(FieldNameFormatter formatter) { - Map> fieldComments = comments.getFieldComments(); - return fieldComments.entrySet().stream() - .map(e -> toFormattedStringCommentEntry(e, formatter)) - .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); - } - - private Map.Entry toFormattedStringCommentEntry( - Map.Entry> entry, FieldNameFormatter formatter - ) { - String fieldComments = commentListToString(entry.getValue()); - String formattedKey = formatter.fromFieldName(entry.getKey()); - return new MapEntry<>(formattedKey, fieldComments); - } - - private String commentListToString(List comments) { - return comments.stream() - .map(this::toCommentLine) - .collect(joining("\n")); - } - - private String toCommentLine(String comment) { - return comment.isEmpty() ? "" : "# " + comment; - } - - private static final class MapEntry implements Map.Entry { - private final K key; - private V value; - - public MapEntry(K key, V value) { - this.key = key; - this.value = value; - } - - @Override - public K getKey() { - return key; - } - - @Override - public V getValue() { - return value; - } - - @Override - public V setValue(V value) { - V old = this.value; - this.value = value; - return old; - } - } - -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlConfiguration.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlConfiguration.java deleted file mode 100644 index 2bb8af4..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlConfiguration.java +++ /dev/null @@ -1,212 +0,0 @@ -package de.exlll.configlib.configs.yaml; - -import de.exlll.configlib.Comments; -import de.exlll.configlib.Configuration; -import de.exlll.configlib.ConfigurationSource; -import de.exlll.configlib.ConfigurationStoreException; -import org.yaml.snakeyaml.DumperOptions; -import org.yaml.snakeyaml.DumperOptions.FlowStyle; -import org.yaml.snakeyaml.constructor.BaseConstructor; -import org.yaml.snakeyaml.constructor.Constructor; -import org.yaml.snakeyaml.representer.Representer; -import org.yaml.snakeyaml.resolver.Resolver; - -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -public abstract class YamlConfiguration extends Configuration { - private final YamlSource source; - - protected YamlConfiguration(Path path, YamlProperties properties) { - super(properties); - this.source = new YamlSource(path, properties); - } - - protected YamlConfiguration(Path path) { - this(path, YamlProperties.DEFAULT); - } - - @Override - protected final ConfigurationSource getSource() { - return source; - } - - @Override - protected final YamlConfiguration getThis() { - return this; - } - - public final void loadAndSave() { - try { - load(); - save(); - } catch (ConfigurationStoreException e) { - if (e.getCause() instanceof NoSuchFileException) { - postLoad(); - save(); - } else { - throw e; - } - } - } - - Comments getComments() { - return comments; - } - - public static class YamlProperties extends Properties { - public static final YamlProperties DEFAULT = builder().build(); - private final List prependedComments; - private final List appendedComments; - private final BaseConstructor constructor; - private final Representer representer; - private final DumperOptions options; - private final Resolver resolver; - - protected YamlProperties(Builder builder) { - super(builder); - this.prependedComments = builder.prependedComments; - this.appendedComments = builder.appendedComments; - this.constructor = builder.constructor; - this.representer = builder.representer; - this.options = builder.options; - this.resolver = builder.resolver; - } - - public static Builder builder() { - return new Builder() { - @Override - protected Builder getThis() { - return this; - } - }; - } - - public final List getPrependedComments() { - return prependedComments; - } - - public final List getAppendedComments() { - return appendedComments; - } - - public final BaseConstructor getConstructor() { - return constructor; - } - - public final Representer getRepresenter() { - return representer; - } - - public final DumperOptions getOptions() { - return options; - } - - public final Resolver getResolver() { - return resolver; - } - - public static abstract class Builder> - extends Properties.Builder { - private List prependedComments = Collections.emptyList(); - private List appendedComments = Collections.emptyList(); - private BaseConstructor constructor = new Constructor(); - private Representer representer = new Representer(); - private DumperOptions options = new DumperOptions(); - private Resolver resolver = new Resolver(); - - protected Builder() { - options.setIndent(2); - options.setDefaultFlowStyle(FlowStyle.BLOCK); - } - - /** - * Sets the comments prepended to a configuration. - * - * @param prependedComments List of comments that are prepended - * @return this {@code Builder} - * @throws NullPointerException if {@code prependedComments ist null} - */ - public final B setPrependedComments(List prependedComments) { - this.prependedComments = Objects.requireNonNull(prependedComments); - return getThis(); - } - - /** - * Sets the comments appended to a configuration. - * - * @param appendedComments List of comments that are appended - * @return this {@code Builder} - * @throws NullPointerException if {@code appendedComments ist null} - */ - public final B setAppendedComments(List appendedComments) { - this.appendedComments = Objects.requireNonNull(appendedComments); - return getThis(); - } - - /** - * Sets the {@link BaseConstructor} used by the underlying YAML-parser. - * - * @param constructor {@code BaseConstructor} used by YAML-parser. - * @return this {@code Builder} - * @throws NullPointerException if {@code constructor ist null} - * @see snakeyaml-Documentation - */ - public final B setConstructor(BaseConstructor constructor) { - this.constructor = Objects.requireNonNull(constructor); - return getThis(); - } - - /** - * Sets the {@link Representer} used by the underlying YAML-parser. - * - * @param representer {@code Representer} used by YAML-parser. - * @return this {@code Builder} - * @throws NullPointerException if {@code representer ist null} - * @see snakeyaml-Documentation - */ - public final B setRepresenter(Representer representer) { - this.representer = Objects.requireNonNull(representer); - return getThis(); - } - - /** - * Sets the {@link DumperOptions} used by the underlying YAML-parser. - * - * @param options {@code DumperOptions} used by YAML-parser. - * @return this {@code Builder} - * @throws NullPointerException if {@code options ist null} - * @see snakeyaml-Documentation - */ - public final B setOptions(DumperOptions options) { - this.options = Objects.requireNonNull(options); - return getThis(); - } - - /** - * Sets the {@link Resolver} used by the underlying YAML-parser. - * - * @param resolver {@code Resolver} used by YAML-parser. - * @return this {@code Builder} - * @throws NullPointerException if {@code resolver ist null} - * @see snakeyaml-Documentation - */ - public final B setResolver(Resolver resolver) { - this.resolver = Objects.requireNonNull(resolver); - return getThis(); - } - - /** - * Builds a new {@code YamlProperties} instance using the values set. - * - * @return new {@code YamlProperties} instance - */ - public YamlProperties build() { - return new YamlProperties(this); - } - } - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlSource.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlSource.java deleted file mode 100644 index d2cc461..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/configs/yaml/YamlSource.java +++ /dev/null @@ -1,138 +0,0 @@ -package de.exlll.configlib.configs.yaml; - -import de.exlll.configlib.Comments; -import de.exlll.configlib.ConfigurationSource; -import de.exlll.configlib.configs.yaml.YamlConfiguration.YamlProperties; -import org.yaml.snakeyaml.Yaml; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static java.util.stream.Collectors.joining; - -final class YamlSource implements ConfigurationSource { - private final Path configPath; - private final YamlProperties props; - private final Yaml yaml; - - public YamlSource(Path configPath, YamlProperties props) { - this.configPath = Objects.requireNonNull(configPath); - this.props = props; - this.yaml = new Yaml( - props.getConstructor(), props.getRepresenter(), - props.getOptions(), props.getResolver() - ); - } - - @Override - public void saveConfiguration(YamlConfiguration config, Map map) - throws IOException { - createParentDirectories(); - CommentAdder adder = new CommentAdder( - yaml.dump(map), config.getComments(), props - ); - String commentedDump = adder.getCommentedDump(); - Files.write(configPath, commentedDump.getBytes()); - } - - private void createParentDirectories() throws IOException { - Path parentDir = configPath.getParent(); - if (!Files.isDirectory(parentDir)) { - Files.createDirectories(parentDir); - } - } - - @Override - public Map loadConfiguration(YamlConfiguration config) - throws IOException { - String cfg = readConfig(); - return yaml.load(cfg); - } - - private String readConfig() throws IOException { - return Files.lines(configPath).collect(joining("\n")); - } - - private static final class CommentAdder { - private static final Pattern PREFIX_PATTERN = Pattern.compile("^\\w+:.*"); - private final String dump; - private final Comments comments; - private final YamlComments yamlComments; - private final YamlProperties props; - private final StringBuilder builder; - - private CommentAdder(String dump, Comments comments, - YamlProperties props - ) { - this.dump = dump; - this.props = props; - this.comments = comments; - this.yamlComments = new YamlComments(comments); - this.builder = new StringBuilder(dump.length()); - } - - public String getCommentedDump() { - addComments(props.getPrependedComments()); - addClassComments(); - addFieldComments(); - addComments(props.getAppendedComments()); - return builder.toString(); - } - - private void addComments(List comments) { - for (String comment : comments) { - if (!comment.isEmpty()) { - builder.append("# ").append(comment); - } - builder.append('\n'); - } - } - - private void addClassComments() { - if (comments.hasClassComments()) { - builder.append(yamlComments.classCommentsAsString()); - builder.append("\n"); - } - } - - private void addFieldComments() { - if (comments.hasFieldComments()) { - List dumpLines = Arrays.asList(dump.split("\n")); - addDumpLines(dumpLines); - } else { - builder.append(dump); - } - } - - private void addDumpLines(List dumpLines) { - for (String dumpLine : dumpLines) { - Matcher m = PREFIX_PATTERN.matcher(dumpLine); - if (m.matches()) { - addFieldComment(dumpLine); - } - builder.append(dumpLine).append('\n'); - } - } - - private void addFieldComment(String dumpLine) { - Map map = yamlComments.fieldCommentAsStrings( - props.getFormatter() - ); - for (Map.Entry entry : map.entrySet()) { - String prefix = entry.getKey() + ":"; - if (dumpLine.startsWith(prefix)) { - builder.append(entry.getValue()).append('\n'); - break; - } - } - - } - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/filter/FieldFilter.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/filter/FieldFilter.java deleted file mode 100644 index 908dee3..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/filter/FieldFilter.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.exlll.configlib.filter; - -import java.lang.reflect.Field; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.function.Predicate; - -import static java.util.stream.Collectors.toList; - -@FunctionalInterface -public interface FieldFilter extends Predicate { - - @Override - default FieldFilter and(Predicate other) { - Objects.requireNonNull(other); - return (t) -> test(t) && other.test(t); - } - - default List filterDeclaredFieldsOf(Class cls) { - Field[] fields = cls.getDeclaredFields(); - return Arrays.stream(fields) - .filter(this) - .collect(toList()); - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/filter/FieldFilters.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/filter/FieldFilters.java deleted file mode 100644 index 6996617..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/filter/FieldFilters.java +++ /dev/null @@ -1,20 +0,0 @@ -package de.exlll.configlib.filter; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -public enum FieldFilters implements FieldFilter { - DEFAULT { - @Override - public boolean test(Field field) { - if (field.isSynthetic()) { - return false; - } - - int mods = field.getModifiers(); - return !(Modifier.isFinal(mods) || - Modifier.isStatic(mods) || - Modifier.isTransient(mods)); - } - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatter.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatter.java deleted file mode 100644 index 2de831e..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatter.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.exlll.configlib.format; - -import java.util.function.Function; - -@FunctionalInterface -public interface FieldNameFormatter extends Function { - String fromFieldName(String fieldName); - - @Override - default String apply(String s) { - return fromFieldName(s); - } -} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatters.java b/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatters.java deleted file mode 100644 index 76e2983..0000000 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/format/FieldNameFormatters.java +++ /dev/null @@ -1,55 +0,0 @@ -package de.exlll.configlib.format; - -public enum FieldNameFormatters implements FieldNameFormatter { - /** - * Represents a {@code FieldNameFormatter} that doesn't actually format the - * field name but instead returns it. - */ - IDENTITY { - @Override - public String fromFieldName(String fn) { - return fn; - } - }, - /** - * Represents a {@code FieldNameFormatter} that transforms camelCase to - * lower_underscore. - *

- * For example, myPrivateField becomes my_private_field. - */ - LOWER_UNDERSCORE { - @Override - public String fromFieldName(String fn) { - StringBuilder builder = new StringBuilder(fn.length()); - for (char c : fn.toCharArray()) { - if (Character.isLowerCase(c)) { - builder.append(c); - } else if (Character.isUpperCase(c)) { - c = Character.toLowerCase(c); - builder.append('_').append(c); - } - } - return builder.toString(); - } - }, - /** - * Represents a {@code FieldNameFormatter} that transforms camelCase to - * UPPER_UNDERSCORE. - *

- * For example, myPrivateField becomes MY_PRIVATE_FIELD. - */ - UPPER_UNDERSCORE { - @Override - public String fromFieldName(String fieldName) { - StringBuilder builder = new StringBuilder(fieldName.length()); - for (char c : fieldName.toCharArray()) { - if (Character.isLowerCase(c)) { - builder.append(Character.toUpperCase(c)); - } else if (Character.isUpperCase(c)) { - builder.append('_').append(c); - } - } - return builder.toString(); - } - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/CommentsTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/CommentsTest.java deleted file mode 100644 index 5301fbb..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/CommentsTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.annotation.Comment; -import org.junit.jupiter.api.Test; - -import static de.exlll.configlib.util.CollectionFactory.listOf; -import static de.exlll.configlib.util.CollectionFactory.mapOf; -import static org.hamcrest.Matchers.empty; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class CommentsTest { - - @Test - void classCommentsAdded() { - class A {} - - @Comment("B") - class B {} - - @Comment({"C", "D"}) - class C {} - - Comments comments = Comments.ofClass(A.class); - assertThat(comments.getClassComments(), empty()); - assertThat(comments.getFieldComments().entrySet(), empty()); - - comments = Comments.ofClass(B.class); - assertThat(comments.getClassComments(), is(listOf("B"))); - assertThat(comments.getFieldComments().entrySet(), empty()); - - comments = Comments.ofClass(C.class); - assertThat(comments.getClassComments(), is(listOf("C", "D"))); - assertThat(comments.getFieldComments().entrySet(), empty()); - } - - @Test - void fieldCommentsAdded() { - class A { - int a; - @Comment("b") - int b; - @Comment({"c", "d"}) - int c; - } - - Comments comments = Comments.ofClass(A.class); - assertThat(comments.getClassComments(), empty()); - assertThat(comments.getFieldComments(), is(mapOf( - "b", listOf("b"), - "c", listOf("c", "d") - ))); - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/ConfigurationTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/ConfigurationTest.java deleted file mode 100644 index 17034a9..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/ConfigurationTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.configs.mem.InSharedMemoryConfiguration; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -class ConfigurationTest { - private static class TestHook extends InSharedMemoryConfiguration { - protected TestHook() { super(Properties.builder().build()); } - } - - @Test - void configExecutesPreSaveHook() { - class A extends TestHook { - int i = 0; - - @Override - protected void preSave() { i++; } - } - A save = new A(); - save.save(); - assertThat(save.i, is(1)); - - A load = new A(); - load.load(); - assertThat(load.i, is(1)); - } - - @Test - void configExecutesPostLoadHook() { - class A extends TestHook { - int i = 0; - - @Override - protected void postLoad() { i++; } - } - A save = new A(); - save.save(); - assertThat(save.i, is(0)); - - A load = new A(); - load.load(); - assertThat(load.i, is(1)); - } -} \ No newline at end of file diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperConverterTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperConverterTest.java deleted file mode 100644 index 580cae5..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperConverterTest.java +++ /dev/null @@ -1,392 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.annotation.Convert; -import de.exlll.configlib.annotation.ElementType; -import de.exlll.configlib.classes.TestSubClass; -import de.exlll.configlib.classes.TestSubClassConverter; -import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.Test; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static de.exlll.configlib.FieldMapperHelpers.*; -import static de.exlll.configlib.util.CollectionFactory.listOf; -import static de.exlll.configlib.util.CollectionFactory.mapOf; -import static de.exlll.configlib.util.CollectionFactory.setOf; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -@SuppressWarnings({"unused", "ThrowableNotThrown"}) -public class FieldMapperConverterTest { - - private static class Point2D { - protected int x = 1; - protected int y = 2; - - private Point2D() {} - - protected Point2D(int x, int y) { - this.x = x; - this.y = y; - } - - private static Point2D of(int x, int y) { - return new Point2D(x, y); - } - - } - - private static final class PointToListConverter - implements Converter> { - @Override - public List convertTo(Point2D element, ConversionInfo info) { - return listOf(element.x, element.y); - } - - @Override - public Point2D convertFrom(List element, ConversionInfo info) { - Point2D point = new Point2D(); - point.x = element.get(0); - point.y = element.get(1); - return point; - } - } - - private static final class PointToMapConverter - implements Converter> { - @Override - public Map convertTo(Point2D element, ConversionInfo info) { - int x = element.x; - int y = element.y; - return mapOf("p", x + ":" + y); - } - - @Override - public Point2D convertFrom(Map element, ConversionInfo info) { - String p = element.get("p"); - String[] split = p.split(":"); - return Point2D.of(Integer.valueOf(split[0]), Integer.valueOf(split[1])); - } - } - - private static final class IntToStringConverter - implements Converter { - - @Override - public String convertTo(Integer element, ConversionInfo info) { - return element.toString(); - } - - @Override - public Integer convertFrom(String element, ConversionInfo info) { - return Integer.valueOf(element); - } - } - - @Test - void instanceToMapRequiresNoArgsConverter() { - class A { - @Convert(MultiArgsConverter.class) - int i; - } - class B { - @Convert(SubConverter.class) - int i; - } - class C { - @Convert(EnumConverter.class) - int i; - } - String msg = "Converter 'MultiArgsConverter' used on field 'i' doesn't " + - "have a no-args constructor."; - assertItmCfgExceptionMessage(new A(), msg); - msg = "Converter 'SubConverter' used on field 'i' doesn't " + - "have a no-args constructor."; - assertItmCfgExceptionMessage(new B(), msg); - msg = "Converter 'EnumConverter' used on field 'i' doesn't " + - "have a no-args constructor."; - assertItmCfgExceptionMessage(new C(), msg); - } - - private static final class MultiArgsConverter - implements Converter { - private MultiArgsConverter(int i) {} - - @Override - public Object convertTo(Object element, ConversionInfo info) { - return null; - } - - @Override - public Object convertFrom(Object element, ConversionInfo info) { - return null; - } - } - - private static final class NullConverter - implements Converter { - - @Override - public String convertTo(String element, ConversionInfo info) { - return null; - } - - @Override - public String convertFrom(String element, ConversionInfo info) { - return null; - } - } - - @Test - void instanceToMapThrowsExceptionIfConverterReturnsNull() { - class A { - @Convert(NullConverter.class) - String s = "string"; - } - String msg = "Failed to convert value 'string' of field 's' because " + - "the converter returned null."; - assertItmCfgExceptionMessage(new A(), msg); - } - - @Test - void instanceFromMapKeepsDefaultValueIfConverterReturnsNull() { - class A { - @Convert(NullConverter.class) - String s = "string"; - } - Map map = mapOf("s", "value"); - A a = instanceFromMap(new A(), map); - assertThat(a.s, is("string")); - } - - private interface SubConverter extends Converter {} - - private enum EnumConverter implements Converter { - ; - - @Override - public Object convertTo(Object element, ConversionInfo info) { - return null; - } - - @Override - public Object convertFrom(Object element, ConversionInfo info) { - return null; - } - } - - @Test - void instanceToMapUsesConverterForSimpleTypes() { - class A { - @Convert(IntToStringConverter.class) - int i = 1; - } - Map map = instanceToMap(new A()); - assertThat(map.get("i"), is("1")); - } - - @Test - void instanceFromMapUsesConverterForSimpleTypes() { - class A { - @Convert(IntToStringConverter.class) - int i = 1; - } - A i = instanceFromMap(new A(), mapOf("i", "10")); - assertThat(i.i, is(10)); - } - - @Test - void instanceToMapConvertsCustomTypesUsingConverters() { - class A { - @Convert(PointToListConverter.class) - Point2D p1 = new Point2D(); - @Convert(PointToMapConverter.class) - Point2D p2 = new Point2D(); - } - Map map = instanceToMap(new A()); - assertThat(map.get("p1"), is(listOf(1, 2))); - assertThat(map.get("p2"), is(mapOf("p", "1:2"))); - } - - @Test - void instanceFromMapConvertsCustomTypesUsingConverters() { - class A { - @Convert(PointToListConverter.class) - Point2D p1 = new Point2D(); - @Convert(PointToMapConverter.class) - Point2D p2 = new Point2D(); - } - Map map = mapOf( - "p1", listOf(10, 11), - "p2", mapOf("p", "11:12") - ); - A i = instanceFromMap(new A(), map); - assertThat(i.p1.x, is(10)); - assertThat(i.p1.y, is(11)); - - assertThat(i.p2.x, is(11)); - assertThat(i.p2.y, is(12)); - } - - private static final class CountingConverter - implements Converter { - static int instanceCount; - - public CountingConverter() { - instanceCount++; - } - - @Override - public Object convertTo(Object element, ConversionInfo info) { - return element; - } - - @Override - public Object convertFrom(Object element, ConversionInfo info) { - return element; - } - } - - @Test - void convertersUseCache() { - class A { - @Convert(CountingConverter.class) - Point2D a = new Point2D(); - @Convert(CountingConverter.class) - Point2D b = new Point2D(); - } - Map map = instanceToMap(new A()); - assertThat(CountingConverter.instanceCount, is(1)); - instanceFromMap(new A(), map); - assertThat(CountingConverter.instanceCount, is(1)); - } - - @Test - void instanceToMapCatchesClassCastException() { - class A { - @Convert(TestSubClassConverter.class) - String s = "string"; - } - String msg = "Converter 'TestSubClassConverter' cannot convert value " + - "'string' of field 's' because it expects a different type."; - assertItmCfgExceptionMessage(new A(), msg); - } - - @Test - void instanceFromMapCatchesClassCastExceptionOfCustomClasses() { - class A { - @Convert(TestSubClassConverter.class) - TestSubClass a = new TestSubClass(); - } - Map map = mapOf( - "a", 1 - ); - String msg = "The value for field 'a' with type 'TestSubClass' " + - "cannot be converted back to its original representation because " + - "a type mismatch occurred."; - assertIfmCfgExceptionMessage(new A(), map, msg); - } - - @Test - void instanceFromMapCatchesClassCastExceptionOfChars() { - class C { - char c; - } - class D { - char d; - } - Map map = mapOf( - "c", "", "d", "12" - ); - String msg = "The value for field 'c' with type 'char' " + - "cannot be converted back to its original representation because " + - "a type mismatch occurred."; - assertIfmCfgExceptionMessage(new C(), map, msg); - - msg = "The value for field 'd' with type 'char' " + - "cannot be converted back to its original representation because " + - "a type mismatch occurred."; - assertIfmCfgExceptionMessage(new D(), map, msg); - } - - @Test - void instanceFromMapCatchesClassCastExceptionOfStrings() { - class B { - String b = "string"; - } - Map map = mapOf( - "b", 2 - ); - String msg = "The value for field 'b' with type 'String' " + - "cannot be converted back to its original representation because " + - "a type mismatch occurred."; - assertIfmCfgExceptionMessage(new B(), map, msg); - } - - @Test - void instanceFromMapCatchesClassCastExceptionOfUnknownEnumConstants() { - class A { - LocalTestEnum e = LocalTestEnum.T; - } - Map map = mapOf( - "e", "V" - ); - String msg = "The value for field 'e' with type 'LocalTestEnum' " + - "cannot be converted back to its original representation because " + - "a type mismatch occurred."; - assertIfmCfgExceptionMessage(new A(), map, msg); - } - - @Test - void instanceFromMapCatchesClassCastExceptionOfUnknownEnumConstantsInLists() { - class A { - @ElementType(value = LocalTestEnum.class, nestingLevel = 1) - List> l = listOf(); - } - Map map = mapOf( - "l", listOf(listOf("Q", "V")) - ); - ConfigurationException ex = assertIfmThrowsCfgException(new A(), map); - Throwable cause = ex.getCause(); - - String msg = "Cannot initialize an enum element of list 'l' because there " + - "is no enum constant 'Q'.\nValid constants are: [S, T]"; - MatcherAssert.assertThat(cause.getMessage(), is(msg)); - } - - @Test - void instanceFromMapCatchesClassCastExceptionOfUnknownEnumConstantsInSets() { - class A { - @ElementType(value = LocalTestEnum.class, nestingLevel = 1) - Set> s = setOf(); - } - Map map = mapOf( - "s", setOf(listOf("Q", "V")) - ); - ConfigurationException ex = assertIfmThrowsCfgException(new A(), map); - Throwable cause = ex.getCause(); - - String msg = "Cannot initialize an enum element of set 's' because there " + - "is no enum constant 'Q'.\nValid constants are: [S, T]"; - MatcherAssert.assertThat(cause.getMessage(), is(msg)); - } - - @Test - void instanceFromMapCatchesClassCastExceptionOfUnknownEnumConstantsInMaps() { - class A { - @ElementType(value = LocalTestEnum.class, nestingLevel = 1) - Map> m = mapOf(); - } - Map map = mapOf( - "m", mapOf(1, listOf("Q", "V")) - ); - ConfigurationException ex = assertIfmThrowsCfgException(new A(), map); - Throwable cause = ex.getCause(); - - String msg = "Cannot initialize an enum element of map 'm' because there " + - "is no enum constant 'Q'.\nValid constants are: [S, T]"; - MatcherAssert.assertThat(cause.getMessage(), is(msg)); - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperHelpers.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperHelpers.java deleted file mode 100644 index 31aa4ea..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperHelpers.java +++ /dev/null @@ -1,91 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.FieldMapper.MappingInfo; -import de.exlll.configlib.annotation.ConfigurationElement; - -import java.util.Map; - -import static de.exlll.configlib.configs.yaml.YamlConfiguration.YamlProperties.DEFAULT; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -public class FieldMapperHelpers { - @ConfigurationElement - interface LocalTestInterface {} - - @ConfigurationElement - static class LocalTestInterfaceImpl implements LocalTestInterface {} - - @ConfigurationElement - static abstract class LocalTestAbstractClass {} - - @ConfigurationElement - static class LocalTestAbstractClassImpl extends LocalTestAbstractClass {} - - @ConfigurationElement - enum LocalTestEnum { - S, T - } - - static class Sub1 {} - - @ConfigurationElement - static class Sub2 {} - - @ConfigurationElement - static class Sub3 { - public Sub3(int x) {} - } - - public static void assertItmCfgExceptionMessage(Object o, String msg) { - ConfigurationException ex = assertItmThrowsCfgException(o); - assertThat(ex.getMessage(), is(msg)); - } - - public static void assertIfmCfgExceptionMessage( - Object o, Map map, String msg - ) { - ConfigurationException ex = assertIfmThrowsCfgException(o, map); - assertThat(ex.getMessage(), is(msg)); - } - - public static ConfigurationException assertItmThrowsCfgException(Object o) { - return assertThrows( - ConfigurationException.class, - () -> instanceToMap(o) - ); - } - - public static ConfigurationException assertIfmThrowsCfgException( - Object o, Map map - ) { - return assertThrows( - ConfigurationException.class, - () -> instanceFromMap(o, map) - ); - } - - public static Map instanceToMap(Object o) { - return instanceToMap(o, DEFAULT); - } - - public static Map instanceToMap( - Object o, Configuration.Properties props - ) { - MappingInfo mappingInfo = new MappingInfo(null, props); - return FieldMapper.instanceToMap(o, mappingInfo); - } - - public static T instanceFromMap(T o, Map map) { - return instanceFromMap(o, map, DEFAULT); - } - - public static T instanceFromMap( - T o, Map map, Configuration.Properties props - ) { - MappingInfo mappingInfo = new MappingInfo(null, props); - FieldMapper.instanceFromMap(o, map, mappingInfo); - return o; - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperTest.java deleted file mode 100644 index ee77a3e..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldMapperTest.java +++ /dev/null @@ -1,592 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.Converter.ConversionInfo; -import de.exlll.configlib.FieldMapper.MappingInfo; -import de.exlll.configlib.annotation.ElementType; -import de.exlll.configlib.annotation.Format; -import de.exlll.configlib.annotation.NoConvert; -import de.exlll.configlib.classes.TestClass; -import de.exlll.configlib.classes.TestSubClass; -import de.exlll.configlib.classes.TestSubClassConverter; -import de.exlll.configlib.configs.mem.InSharedMemoryConfiguration; -import de.exlll.configlib.configs.yaml.YamlConfiguration.YamlProperties; -import de.exlll.configlib.format.FieldNameFormatter; -import de.exlll.configlib.format.FieldNameFormatters; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Field; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -import static de.exlll.configlib.Converters.ENUM_CONVERTER; -import static de.exlll.configlib.Converters.SIMPLE_TYPE_CONVERTER; -import static de.exlll.configlib.FieldMapperHelpers.*; -import static de.exlll.configlib.util.CollectionFactory.*; -import static java.util.stream.Collectors.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertThrows; - -@SuppressWarnings({"unused", "ThrowableNotThrown"}) -class FieldMapperTest { - private static final TestClass t = TestClass.TEST_VALUES; - private static final Configuration.Properties DEFAULT = - Configuration.Properties.builder().build(); - private static final YamlProperties WITH_UPPER_FORMATTER = YamlProperties - .builder() - .setFormatter(FieldNameFormatters.UPPER_UNDERSCORE) - .build(); - private static final Map map = instanceToMap( - TestClass.TEST_VALUES, DEFAULT - ); - - private TestClass tmp; - - @BeforeEach - void setUp() { - tmp = new TestClass(); - MappingInfo mappingInfo = MappingInfo.from(tmp); - FieldMapper.instanceFromMap(tmp, map, mappingInfo); - } - - @Test - void instanceFromMapSetsSimpleTypes() { - assertAll( - () -> assertThat(tmp.getPrimBool(), is(t.getPrimBool())), - () -> assertThat(tmp.getRefBool(), is(t.getRefBool())), - () -> assertThat(tmp.getPrimByte(), is(t.getPrimByte())), - () -> assertThat(tmp.getRefByte(), is(t.getRefByte())), - () -> assertThat(tmp.getPrimChar(), is(t.getPrimChar())), - () -> assertThat(tmp.getRefChar(), is(t.getRefChar())), - () -> assertThat(tmp.getPrimShort(), is(t.getPrimShort())), - () -> assertThat(tmp.getRefShort(), is(t.getRefShort())), - () -> assertThat(tmp.getPrimInt(), is(t.getPrimInt())), - () -> assertThat(tmp.getRefInt(), is(t.getRefInt())), - () -> assertThat(tmp.getPrimLong(), is(t.getPrimLong())), - () -> assertThat(tmp.getRefLong(), is(t.getRefLong())), - () -> assertThat(tmp.getPrimFloat(), is(t.getPrimFloat())), - () -> assertThat(tmp.getRefFloat(), is(t.getRefFloat())), - () -> assertThat(tmp.getPrimDouble(), is(t.getPrimDouble())), - () -> assertThat(tmp.getRefDouble(), is(t.getRefDouble())), - () -> assertThat(tmp.getString(), is(t.getString())) - ); - } - - @Test - void instanceFromMapSetsEnums() { - assertThat(tmp.getE1(), is(t.getE1())); - } - - @Test - void instanceFromMapSetsContainersOfSimpleTypes() { - assertAll( - () -> assertThat(tmp.getInts(), is(t.getInts())), - () -> assertThat(tmp.getStrings(), is(t.getStrings())), - () -> assertThat(tmp.getDoubleByBool(), is(t.getDoubleByBool())) - ); - } - - @Test - void instanceFromMapsConvertsMapsToTypes() { - assertThat(tmp.getSubClass(), is(t.getSubClass())); - } - - @Test - void instanceFromMapsConvertsExcludedClasses() { - assertThat(tmp.getExcludedClass(), is(t.getExcludedClass())); - } - - @Test - void instanceFromMapsConvertsContainersOfMaps() { - assertThat(tmp.getSubClassList(), is(t.getSubClassList())); - assertThat(tmp.getSubClassSet(), is(t.getSubClassSet())); - assertThat(tmp.getSubClassMap(), is(t.getSubClassMap())); - assertThat(tmp.getSubClassListsList(), is(t.getSubClassListsList())); - assertThat(tmp.getSubClassSetsSet(), is(t.getSubClassSetsSet())); - assertThat(tmp.getSubClassMapsMap(), is(t.getSubClassMapsMap())); - } - - @Test - void instanceFromMapDoesNotSetFinalStaticOrTransientFields() { - Map map = mapOf( - "staticFinalInt", 10, - "staticInt", 10, - "finalInt", 10, - "transientInt", 10 - ); - TestClass cls = instanceFromMap(new TestClass(), map); - assertThat(TestClass.getStaticFinalInt(), is(1)); - assertThat(TestClass.getStaticInt(), is(2)); - assertThat(cls.getFinalInt(), is(3)); - assertThat(cls.getTransientInt(), is(4)); - } - - @Test - void instanceFromMapConvertsAllFields() { - assertThat(tmp, is(t)); - } - - @Test - void instanceFromMapThrowsExceptionIfEnumConstantDoesNotExist() { - class A { - LocalTestEnum t = LocalTestEnum.T; - } - Map map = mapOf( - "t", "R" - ); - - String msg = "Cannot initialize enum 't' because there is no enum " + - "constant 'R'.\nValid constants are: [S, T]"; - assertThrows( - IllegalArgumentException.class, - () -> ((Converter, String>) ENUM_CONVERTER) - .convertFrom("R", newInfo("t", new A())), - msg - ); - } - - @Test - void instanceFromMapIgnoresNullValues() { - class A { - TestSubClass c = new TestSubClass(); - } - Map map = mapOf("c", mapOf("primInt", 20)); - - A a = new A(); - assertThat(a.c.getPrimInt(), is(0)); - assertThat(a.c.getString(), is("")); - instanceFromMap(a, map); - assertThat(a.c.getPrimInt(), is(20)); - assertThat(a.c.getString(), is("")); - } - - @Test - void instanceFromMapSetsField() { - TestClass ins = TestClass.TEST_VALUES; - TestClass def = new TestClass(); - assertThat(ins, is(not(def))); - instanceFromMap(def, map); - assertThat(ins, is(def)); - } - - @Test - void instanceFromMapCreatesConcreteInstances() { - class A { - LocalTestInterface l = new LocalTestInterfaceImpl(); - } - class B { - LocalTestAbstractClass l = new LocalTestAbstractClassImpl(); - } - instanceFromMap(new A(), mapOf("l", mapOf())); - instanceFromMap(new B(), mapOf("l", mapOf())); - } - - - @Test - void instanceToMapUsesFieldNameFormatter() { - Configuration.Properties.Builder builder = - Configuration.Properties.builder(); - Map map = instanceToMap( - TestClass.TEST_VALUES, - builder.setFormatter(FieldNameFormatters.LOWER_UNDERSCORE).build() - ); - assertThat(map.get("primBool"), nullValue()); - assertThat(map.get("prim_bool"), is(true)); - } - - @Test - void instanceToMapContainsAllFields() { - assertThat(map.size(), is(34)); - } - - @Test - void instanceToMapDoesNotContainFinalStaticOrTransientFields() { - assertAll( - () -> assertThat(map.get("staticFinalInt"), is(nullValue())), - () -> assertThat(map.get("staticInt"), is(nullValue())), - () -> assertThat(map.get("finalInt"), is(nullValue())), - () -> assertThat(map.get("transientInt"), is(nullValue())) - ); - } - - @Test - void instanceToMapContainsAllSimpleFields() { - assertAll( - () -> assertThat(map.get("primBool"), is(t.getPrimBool())), - () -> assertThat(map.get("refBool"), is(t.getRefBool())), - () -> assertThat(map.get("primByte"), is(t.getPrimByte())), - () -> assertThat(map.get("refByte"), is(t.getRefByte())), - () -> assertThat(map.get("primChar"), is(t.getPrimChar())), - () -> assertThat(map.get("refChar"), is(t.getRefChar())), - () -> assertThat(map.get("primShort"), is(t.getPrimShort())), - () -> assertThat(map.get("refShort"), is(t.getRefShort())), - () -> assertThat(map.get("primInt"), is(t.getPrimInt())), - () -> assertThat(map.get("refInt"), is(t.getRefInt())), - () -> assertThat(map.get("primLong"), is(t.getPrimLong())), - () -> assertThat(map.get("refLong"), is(t.getRefLong())), - () -> assertThat(map.get("primFloat"), is(t.getPrimFloat())), - () -> assertThat(map.get("refFloat"), is(t.getRefFloat())), - () -> assertThat(map.get("primDouble"), is(t.getPrimDouble())), - () -> assertThat(map.get("refDouble"), is(t.getRefDouble())), - () -> assertThat(map.get("string"), is(t.getString())) - ); - } - - @Test - void instanceToMapContainsAllContainersOfSimpleTypes() { - assertAll( - () -> assertThat(map.get("ints"), is(t.getInts())), - () -> assertThat(map.get("strings"), is(t.getStrings())), - () -> assertThat(map.get("doubleByBool"), is(t.getDoubleByBool())) - ); - } - - @Test - void instanceToMapConvertsTypesToMaps() { - assertThat(map.get("subClass"), is(t.getSubClass().asMap())); - } - - @Test - void instanceToMapConvertsExcludedClasses() { - assertThat(map.get("excludedClass"), is(t.getExcludedClass())); - } - - @Test - void instanceToMapConvertsEnumsContainersToStringContainers() { - class A { - @ElementType(LocalTestEnum.class) - List el = listOf(LocalTestEnum.S, LocalTestEnum.T); - @ElementType(LocalTestEnum.class) - Set es = setOf(LocalTestEnum.S, LocalTestEnum.T); - @ElementType(LocalTestEnum.class) - Map em = mapOf( - "1", LocalTestEnum.S, "2", LocalTestEnum.T - ); - } - Map map = instanceToMap(new A()); - assertThat(map.get("el"), is(listOf("S", "T"))); - assertThat(map.get("es"), is(setOf("S", "T"))); - assertThat(map.get("em"), is(mapOf("1", "S", "2", "T"))); - } - - @Test - void instanceFromMapConvertsStringContainersToEnumContainers() { - class A { - @ElementType(LocalTestEnum.class) - List el = listOf(); - @ElementType(LocalTestEnum.class) - Set es = setOf(); - @ElementType(LocalTestEnum.class) - Map em = mapOf(); - } - Map map = mapOf( - "el", listOf("S", "T"), - "es", setOf("S", "T"), - "em", mapOf("1", "S", "2", "T") - ); - A a = instanceFromMap(new A(), map); - assertThat(a.el, is(listOf(LocalTestEnum.S, LocalTestEnum.T))); - assertThat(a.es, is(setOf(LocalTestEnum.S, LocalTestEnum.T))); - assertThat(a.em, is(mapOf( - "1", LocalTestEnum.S, "2", LocalTestEnum.T - ))); - } - - - @Test - void instanceToMapConvertsContainerElementsToMaps() { - List> subClassList = t.getSubClassList().stream() - .map(TestSubClass::asMap) - .collect(toList()); - Set> subClassSet = t.getSubClassSet().stream() - .map(TestSubClass::asMap) - .collect(toSet()); - Map> subClassMap = t.getSubClassMap() - .entrySet().stream() - .map(e -> mapEntry(e.getKey(), e.getValue().asMap())) - .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); - assertAll( - () -> assertThat(map.get("subClassSet"), is(subClassSet)), - () -> assertThat(map.get("subClassMap"), is(subClassMap)), - () -> assertThat(map.get("subClassList"), is(subClassList)) - ); - } - - @Test - void instanceToMapContainsNestedContainersOfSimpleTypes() { - assertAll( - () -> assertThat(map.get("listsList"), is(t.getListsList())), - () -> assertThat(map.get("setsSet"), is(t.getSetsSet())), - () -> assertThat(map.get("mapsMap"), is(t.getMapsMap())) - ); - } - - @Test - void instanceToMapContainsNestedContainersOfCustomTypes() { - List>> lists = t.getSubClassListsList() - .stream().map(list -> list.stream() - .map(TestSubClass::asMap) - .collect(toList())) - .collect(toList()); - Set>> sets = t.getSubClassSetsSet() - .stream().map(set -> set.stream() - .map(TestSubClass::asMap) - .collect(toSet())) - .collect(toSet()); - Function, Map>> f = - map -> map.entrySet().stream().collect( - toMap(Map.Entry::getKey, e -> e.getValue().asMap()) - ); - Map>> m = t.getSubClassMapsMap() - .entrySet().stream() - .map(e -> mapEntry(e.getKey(), f.apply(e.getValue()))) - .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); - - assertThat(map.get("subClassListsList"), is(lists)); - assertThat(map.get("subClassSetsSet"), is(sets)); - assertThat(map.get("subClassMapsMap"), is(m)); - } - - @Test - void instanceToMapContainsEnums() { - assertThat(map.get("e1"), is(t.getE1().toString())); - } - - @Test - void instanceToMapContainsEnumLists() { - List list = t.getEnums().stream() - .map(Enum::toString) - .collect(toList()); - assertThat(map.get("enums"), is(list)); - } - - - @Test - void instanceToMapConvertsConvertTypes() { - String s = new TestSubClassConverter() - .convertTo(t.getConverterSubClass(), null); - assertThat(map.get("converterSubClass"), is(s)); - } - - @Test - void instanceToMapCreatesLinkedHashMap() { - assertThat(instanceToMap(new Object()), instanceOf(LinkedHashMap.class)); - } - - private static Converter converter = SIMPLE_TYPE_CONVERTER; - - private static ConversionInfo newInfo(String fieldName, Object o) { - Field field = null; - try { - field = o.getClass().getDeclaredField(fieldName); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } - MappingInfo mappingInfo = new MappingInfo(null, WITH_UPPER_FORMATTER); - return ConversionInfo.from(field, o, mappingInfo); - } - - private static ConversionInfo newInfo(String fieldName) { - return newInfo(fieldName, new TestClass()); - } - - @Test - void typeConverterReturnsInstanceIfClassesMatch() { - //noinspection RedundantStringConstructorCall - String s = new String("123"); - Object val = converter.convertFrom(s, newInfo("string")); - assertThat(val, sameInstance(s)); - } - - @Test - void typeConverterConvertsStringToCharacter() { - String s = "1"; - Object vc = converter.convertFrom(s, newInfo("primChar")); - Object vd = converter.convertFrom(s, newInfo("refChar")); - - assertThat(vc, instanceOf(Character.class)); - assertThat(vd, instanceOf(Character.class)); - } - - @Test - void typeConverterChecksInvalidStrings() { - String msg = "An empty string cannot be converted to a character."; - assertThrows( - IllegalArgumentException.class, - () -> converter.convertFrom("", newInfo("refChar")), - msg - ); - - String value = "123"; - msg = "String '" + value + "' is too long to be converted to a character"; - assertThrows( - IllegalArgumentException.class, - () -> converter.convertFrom(value, newInfo("refChar")), - msg - ); - } - - @Test - void typeConverterConvertsNumbers() { - Number[] numbers = { - (byte) 1, (short) 2, 3, (long) 4, - (float) 5, (double) 6, 4L, 5f, 6d - }; - String[] classes = { - "refByte", "refShort", "refInt", - "primLong", "refFloat", "refDouble" - }; - - for (String cls : classes) { - for (Number number : numbers) { - ConversionInfo info = newInfo(cls); - Object converted = converter.convertFrom(number, info); - assertThat(converted, instanceOf(info.getFieldType())); - } - } - } - - @Test - void typeConverterChecksInvalidNumbers() { - String msg = "Number '1' cannot be converted to type 'String'"; - assertThrows( - IllegalArgumentException.class, - () -> converter.convertFrom(1, newInfo("string")), - msg - ); - } - - private static final class ExcludedClass {} - - @Test - void instanceToMapSetsAutoConvertedInstancesAsIs() { - class A { - @NoConvert - ExcludedClass ex = new ExcludedClass(); - } - A a = new A(); - Map map = instanceToMap(a); - assertThat(map.get("ex"), sameInstance(a.ex)); - } - - @Test - void instanceFromMapSetsAutoConvertedInstancesAsIs() { - class A { - @NoConvert - ExcludedClass ex = new ExcludedClass(); - } - - ExcludedClass cls = new ExcludedClass(); - Map map = mapOf("ex", cls); - A a = instanceFromMap(new A(), map); - assertThat(a.ex, sameInstance(cls)); - } - - @Test - void fieldMapperUsesFiltersAdded() { - class A { - private int a = 1; - private int b = 2; - private int c = 3; - private transient int d = 4; - private final int e = 5; - } - Configuration.Properties props = Configuration.Properties.builder() - .addFilter(field -> !field.getName().equals("a")) - .addFilter(field -> !field.getName().equals("c")) - .build(); - - Map map = instanceToMap(new A(), props); - assertThat(map.size(), is(1)); - assertThat(map, is(mapOf("b", 2))); - - map = mapOf("a", -1, "b", -2, "c", -3, "d", -4, "e", -5); - A a = instanceFromMap(new A(), map, props); - assertThat(a.a, is(1)); - assertThat(a.b, is(-2)); - assertThat(a.c, is(3)); - assertThat(a.d, is(4)); - assertThat(a.e, is(5)); - } - - @Test - void selectFieldNameFormatterReturnsPropertiesIfNoFormatAnnotation() { - class A extends InSharedMemoryConfiguration { - protected A() { super(WITH_UPPER_FORMATTER); } - } - A a = new A(); - FieldNameFormatter fnf = FieldMapper.selectFormatter(MappingInfo.from(a)); - assertThat(fnf, sameInstance(FieldNameFormatters.UPPER_UNDERSCORE)); - } - - @Test - void selectFieldNameFormatterReturnsIfFormatAnnotation() { - @Format(FieldNameFormatters.LOWER_UNDERSCORE) - class A extends InSharedMemoryConfiguration { - protected A() { super(WITH_UPPER_FORMATTER); } - } - @Format(formatterClass = MyFormatter.class) - class B extends InSharedMemoryConfiguration { - protected B() { super(WITH_UPPER_FORMATTER); } - } - A a = new A(); - FieldNameFormatter fnf = FieldMapper.selectFormatter(MappingInfo.from(a)); - assertThat(fnf, sameInstance(FieldNameFormatters.LOWER_UNDERSCORE)); - - B b = new B(); - fnf = FieldMapper.selectFormatter(MappingInfo.from(b)); - assertThat(fnf, instanceOf(MyFormatter.class)); - } - - @Test - void formatterClassTakesPrecedence() { - @Format( - value = FieldNameFormatters.LOWER_UNDERSCORE, - formatterClass = MyFormatter.class - ) - class A extends InSharedMemoryConfiguration { - protected A() { super(WITH_UPPER_FORMATTER); } - } - A a = new A(); - FieldNameFormatter fnf = FieldMapper.selectFormatter(MappingInfo.from(a)); - assertThat(fnf, instanceOf(MyFormatter.class)); - } - - private static final class MyFormatter implements FieldNameFormatter { - @Override - public String fromFieldName(String fieldName) { - return fieldName; - } - } - - @Test - void fieldMapperUsesSelectFieldNameFormatter() { - @Format(FieldNameFormatters.LOWER_UNDERSCORE) - class A extends InSharedMemoryConfiguration { - private int abc = 1; - private int eFg = 2; - private int hIJ = 3; - - protected A() { super(WITH_UPPER_FORMATTER); } - } - - A a = new A(); - MappingInfo mappingInfo = MappingInfo.from(a); - Map map = FieldMapper.instanceToMap(a, mappingInfo); - - assertThat(map.get("abc"), is(1)); - assertThat(map.get("e_fg"), is(2)); - assertThat(map.get("h_i_j"), is(3)); - - map = mapOf("abc", 4, "e_fg", 5, "h_i_j", 6); - FieldMapper.instanceFromMap(a, map, mappingInfo); - assertThat(a.abc, is(4)); - assertThat(a.eFg, is(5)); - assertThat(a.hIJ, is(6)); - } -} \ No newline at end of file diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldNameFormattersTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldNameFormattersTest.java deleted file mode 100644 index a20f975..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/FieldNameFormattersTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.format.FieldNameFormatter; -import de.exlll.configlib.format.FieldNameFormatters; -import org.junit.jupiter.api.Test; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -class FieldNameFormattersTest { - @Test - void identityReturnsSameName() { - FieldNameFormatter formatter = FieldNameFormatters.IDENTITY; - - assertThat(formatter.fromFieldName("fieldName"), is("fieldName")); - } - - @Test - void lowerUnderscoreConvertsFromAndToCamelCase() { - FieldNameFormatter formatter = FieldNameFormatters.LOWER_UNDERSCORE; - - assertThat(formatter.fromFieldName("fieldNameFormat"), is("field_name_format")); - } - - @Test - void upperUnderscoreConvertsFromAndToCamelCase() { - FieldNameFormatter formatter = FieldNameFormatters.UPPER_UNDERSCORE; - - assertThat(formatter.fromFieldName("fieldNameFormat"), is("FIELD_NAME_FORMAT")); - } -} \ No newline at end of file diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/ReflectTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/ReflectTest.java deleted file mode 100644 index bf6cb09..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/ReflectTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.classes.TestClass; -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -import static de.exlll.configlib.util.CollectionFactory.setOf; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -class ReflectTest { - private static final Set> ALL_SIMPLE_TYPES = setOf( - boolean.class, Boolean.class, - byte.class, Byte.class, - char.class, Character.class, - short.class, Short.class, - int.class, Integer.class, - long.class, Long.class, - float.class, Float.class, - double.class, Double.class, - String.class - ); - - @Test - void isSimpleType() { - for (Class cls : ALL_SIMPLE_TYPES) { - assertThat(Reflect.isSimpleType(cls), is(true)); - } - assertThat(Reflect.isSimpleType(Object.class), is(false)); - } - - @Test - void isContainerType() { - assertThat(Reflect.isContainerType(Object.class), is(false)); - assertThat(Reflect.isContainerType(HashMap.class), is(true)); - assertThat(Reflect.isContainerType(HashSet.class), is(true)); - assertThat(Reflect.isContainerType(ArrayList.class), is(true)); - } - - @Test - void getValue() throws NoSuchFieldException { - TestClass testClass = TestClass.TEST_VALUES; - Field f1 = TestClass.class.getDeclaredField("string"); - Field f2 = TestClass.class.getDeclaredField("primLong"); - Field f3 = TestClass.class.getDeclaredField("staticFinalInt"); - - Object value = Reflect.getValue(f1, testClass); - assertThat(value, is(testClass.getString())); - - value = Reflect.getValue(f2, testClass); - assertThat(value, is(testClass.getPrimLong())); - - value = Reflect.getValue(f3, testClass); - assertThat(value, is(TestClass.getStaticFinalInt())); - } -} \ No newline at end of file diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/ValidatorTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/ValidatorTest.java deleted file mode 100644 index 9ce9a21..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/ValidatorTest.java +++ /dev/null @@ -1,667 +0,0 @@ -package de.exlll.configlib; - -import de.exlll.configlib.annotation.ConfigurationElement; -import de.exlll.configlib.annotation.ElementType; -import de.exlll.configlib.classes.TestSubClass; -import org.junit.jupiter.api.Test; - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.CopyOnWriteArrayList; - -import static de.exlll.configlib.FieldMapperHelpers.*; -import static de.exlll.configlib.util.CollectionFactory.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -@SuppressWarnings("unused") -public class ValidatorTest { - @Test - void instanceFromMapRequiresMapToInitializeCustomClass() { - class A { - TestSubClass c = new TestSubClass(); - } - - Map map = mapOf( - "c", "s" - ); - String msg = "Initializing field 'c' requires a Map " + - "but the given object is not a map." + - "\nType: 'String'\tValue: 's'"; - assertIfmCfgExceptionMessage(new A(), map, msg); - } - - @Test - void instanceFromMapChecksEnumValuesAreString() { - class A { - LocalTestEnum t = LocalTestEnum.T; - } - Map map = mapOf( - "t", 1 - ); - String msg = "Initializing enum 't' requires a string but '1' is of " + - "type 'Integer'."; - assertIfmCfgExceptionMessage(new A(), map, msg); - } - - @Test - void instanceFromMapRequiresMapWithStringsAsKeys() { - class A { - TestSubClass c = new TestSubClass(); - } - - Map map = mapOf( - "c", mapOf(1, 200, "string", "s") - ); - String msg = "Initializing field 'c' requires a Map " + - "but the given map contains non-string keys." + - "\nAll entries: " + map.get("c"); - assertIfmCfgExceptionMessage(new A(), map, msg); - } - - - @Test - void instanceToMapRequiresNonNullMapKeys() { - class A { - TestSubClass c = new TestSubClass(); - } - Map m1 = new HashMap<>(); - m1.put(null, "null"); - Map m2 = mapOf("c", m1); - String msg = "Initializing field 'c' requires a Map " + - "but the given map contains non-string keys." + - "\nAll entries: {null=null}"; - assertIfmCfgExceptionMessage(new A(), m2, msg); - } - - @Test - void instanceFromMapRequiresCustomClassToHaveNoArgsConstructors() { - class A { - Sub3 s = new Sub3(1); - } - Map map = mapOf("s", mapOf()); - String msg = "Type 'Sub3' of field 's' doesn't have a no-args constructor."; - assertIfmCfgExceptionMessage(new A(), map, msg); - } - - @Test - void instanceFromMapRequiresCustomClassToBeConfigurationElements() { - class A { - Sub1 s = new Sub1(); - } - Map map = mapOf("s", mapOf()); - String msg = "Type 'Sub1' of field 's' is not annotated " + - "as a configuration element."; - assertIfmCfgExceptionMessage(new A(), map, msg); - } - - @Test - void instanceFromMapChecksThatContainerTypesMatch() { - class A { - CopyOnWriteArrayList l = new CopyOnWriteArrayList<>(); - } - class B { - ConcurrentSkipListSet s = new ConcurrentSkipListSet<>(); - } - class C { - ConcurrentHashMap m = new ConcurrentHashMap<>(); - } - Map m = mapOf("l", listOf("s")); - String msg = "Can not set field 'l' with type 'CopyOnWriteArrayList' " + - "to 'ArrayList'."; - assertIfmCfgExceptionMessage(new A(), m, msg); - - m = mapOf("s", setOf("s")); - msg = "Can not set field 's' with type 'ConcurrentSkipListSet' " + - "to 'LinkedHashSet'."; - assertIfmCfgExceptionMessage(new B(), m, msg); - - m = mapOf("m", mapOf(1, "s")); - msg = "Can not set field 'm' with type 'ConcurrentHashMap' " + - "to 'LinkedHashMap'."; - assertIfmCfgExceptionMessage(new C(), m, msg); - - } - - @Test - void instanceToMapThrowsExceptionIfDefaultValueIsNull() { - class A { - String string; - } - String msg = "The value of field 'string' is null.\n" + - "Please assign a non-null default value or remove this field."; - assertItmCfgExceptionMessage(new A(), msg); - } - - @Test - void instanceFromMapThrowsExceptionIfDefaultValueIsNull() { - class A { - String string; - } - Map map = mapOf("string", "s"); - String msg = "The value of field 'string' is null.\n" + - "Please assign a non-null default value or remove this field."; - assertIfmCfgExceptionMessage(new A(), map, msg); - } - - @Test - void instanceToMapRequiresListsWithoutElementTypeToContainSimpleTypes() { - class A { - List l = new ArrayList<>(listOf( - TestSubClass.TEST_VALUES - )); - } - class B { - List>> l = new ArrayList<>(listOf( - setOf(mapOf(1, TestSubClass.TEST_VALUES)) - )); - } - - String asString = TestSubClass.TEST_VALUES.toString(); - - A a = new A(); - String msg = "The type of an element of list 'l' is not a simple type " + - "but list 'l' is missing the ElementType annotation.\n" + - "All elements: [" + asString + "]"; - assertItmCfgExceptionMessage(a, msg); - - B b = new B(); - msg = "The type of an element of list 'l' is not a simple type " + - "but list 'l' is missing the ElementType annotation.\n" + - "All elements: [[{1=" + asString + "}]]"; - assertItmCfgExceptionMessage(b, msg); - } - - @Test - void instanceToMapRequiresSetsWithoutElementTypeToContainSimpleTypes() { - class A { - Set s = new HashSet<>(setOf( - TestSubClass.TEST_VALUES - )); - } - class B { - Set>> s = new HashSet<>(setOf( - listOf(mapOf(1, TestSubClass.TEST_VALUES)) - )); - } - - String asString = TestSubClass.TEST_VALUES.toString(); - - A a = new A(); - String msg = "The type of an element of set 's' is not a simple type " + - "but set 's' is missing the ElementType annotation.\n" + - "All elements: [" + asString + "]"; - assertItmCfgExceptionMessage(a, msg); - - B b = new B(); - msg = "The type of an element of set 's' is not a simple type " + - "but set 's' is missing the ElementType annotation.\n" + - "All elements: [[{1=" + asString + "}]]"; - assertItmCfgExceptionMessage(b, msg); - } - - @Test - void instanceToMapRequiresMapsWithoutElementTypeToContainSimpleTypes() { - class A { - Map m = new HashMap<>(mapOf( - 1, TestSubClass.TEST_VALUES - )); - } - class B { - Map>> m = new HashMap<>(mapOf( - 1, setOf(listOf(TestSubClass.TEST_VALUES)) - )); - } - - String asString = TestSubClass.TEST_VALUES.toString(); - - A a = new A(); - String msg = "The type of a value of map 'm' is not a simple type " + - "but map 'm' is missing the ElementType annotation.\n" + - "All entries: {1=" + asString + "}"; - assertItmCfgExceptionMessage(a, msg); - - B b = new B(); - msg = "The type of a value of map 'm' is not a simple type " + - "but map 'm' is missing the ElementType annotation.\n" + - "All entries: {1=[[" + asString + "]]}"; - assertItmCfgExceptionMessage(b, msg); - } - - @Test - void instanceToMapRequiresNonNullListElements() { - class A { - @ElementType(TestSubClass.class) - List l1 = new ArrayList<>(); - List l2 = new ArrayList<>(); - } - A a = new A(); - a.l1.add(null); - a.l2.add(null); - - String msg = "An element of list 'l1' is null.\n" + - "Please either remove or replace this element.\n" + - "All elements: [null]"; - assertItmCfgExceptionMessage(a, msg); - - a.l1.clear(); - msg = "An element of list 'l2' is null.\n" + - "Please either remove or replace this element.\n" + - "All elements: [null]"; - assertItmCfgExceptionMessage(a, msg); - } - - @Test - void instanceToMapRequiresNonNullListElementsRecursively() { - class A { - @ElementType(TestSubClass.class) - List> bla = new ArrayList<>(); - } - A o = new A(); - o.bla.add(new ArrayList<>()); - o.bla.get(0).add(null); - String msg = "An element of list 'bla' is null.\n" + - "Please either remove or replace this element.\n" + - "All elements: [[null]]"; - assertItmCfgExceptionMessage(o, msg); - } - - @Test - void instanceToMapRequiresNonNullSetElements() { - class A { - @ElementType(TestSubClass.class) - Set s1 = new HashSet<>(); - Set s2 = new HashSet<>(); - } - A a = new A(); - a.s1.add(null); - a.s2.add(null); - - String msg = "An element of set 's1' is null.\n" + - "Please either remove or replace this element.\n" + - "All elements: [null]"; - assertItmCfgExceptionMessage(a, msg); - - a.s1.clear(); - msg = "An element of set 's2' is null.\n" + - "Please either remove or replace this element.\n" + - "All elements: [null]"; - assertItmCfgExceptionMessage(a, msg); - } - - @Test - void instanceToMapRequiresNonNullSetElementsRecursively() { - class A { - @ElementType(TestSubClass.class) - Set> bla = new HashSet<>(); - } - A o = new A(); - o.bla.add(new ArrayList<>()); - o.bla.iterator().next().add(null); - String msg = "An element of set 'bla' is null.\n" + - "Please either remove or replace this element.\n" + - "All elements: [[null]]"; - assertItmCfgExceptionMessage(o, msg); - } - - - @Test - void instanceToMapRequiresNonNullMapValues() { - class A { - @ElementType(TestSubClass.class) - Map m1 = new HashMap<>(); - Map m2 = new HashMap<>(); - } - A a = new A(); - a.m1.put(1, null); - a.m2.put(1, null); - - String msg = "A value of map 'm1' is null.\n" + - "Please either remove or replace this element.\n" + - "All entries: {1=null}"; - assertItmCfgExceptionMessage(a, msg); - - a.m1.clear(); - msg = "A value of map 'm2' is null.\n" + - "Please either remove or replace this element.\n" + - "All entries: {1=null}"; - assertItmCfgExceptionMessage(a, msg); - } - - @Test - void instanceToMapRequiresNonNullMapValuesRecursively() { - class A { - @ElementType(TestSubClass.class) - Map> bla = new HashMap<>(); - } - A o = new A(); - o.bla.put(1, new ArrayList<>()); - o.bla.get(1).add(null); - String msg = "A value of map 'bla' is null.\n" + - "Please either remove or replace this element.\n" + - "All entries: {1=[null]}"; - assertItmCfgExceptionMessage(o, msg); - } - - @Test - void instanceToMapRequiresSimpleMapKeys() { - class A { - Map m = new HashMap<>(); - } - A a = new A(); - a.m.put(TestSubClass.TEST_VALUES, 1); - - String msg = "The keys of map 'm' must be simple types."; - assertItmCfgExceptionMessage(a, msg); - } - - @Test - void instanceToMapRequiresContainerTypesToMatchElementType() { - class A { - @ElementType(TestSubClass.class) - List l = new ArrayList<>(); - @ElementType(TestSubClass.class) - Set s = new HashSet<>(); - @ElementType(TestSubClass.class) - Map m = new HashMap<>(); - } - A a = new A(); - a.l.add(1); - a.s.add(1); - a.m.put(1, 1); - - String msg = "The type of an element of list 'l' doesn't match the " + - "type indicated by the ElementType annotation.\n" + - "Required type: 'TestSubClass'\tActual type: 'Integer'\n" + - "All elements: [1]"; - assertItmCfgExceptionMessage(a, msg); - - a.l.clear(); - msg = "The type of an element of set 's' doesn't match the " + - "type indicated by the ElementType annotation.\n" + - "Required type: 'TestSubClass'\tActual type: 'Integer'\n" + - "All elements: [1]"; - assertItmCfgExceptionMessage(a, msg); - - a.s.clear(); - msg = "The type of a value of map 'm' doesn't match the " + - "type indicated by the ElementType annotation.\n" + - "Required type: 'TestSubClass'\tActual type: 'Integer'\n" + - "All entries: {1=1}"; - assertItmCfgExceptionMessage(a, msg); - } - - @Test - void instanceToMapRequiresCustomClassesToBeConfigurationElements() { - class A { - Sub1 s = new Sub1(); - } - class B { - Sub2 s = new Sub2(); - } - - Map map = mapOf("s", Collections.emptyMap()); - - assertThat(instanceToMap(new B()), is(map)); - - String msg = "Type 'Sub1' of field 's' is not annotated " + - "as a configuration element."; - assertItmCfgExceptionMessage(new A(), msg); - } - - @Test - void instanceToMapRequiresElementTypesToBeConcreteType() { - class A { - @ElementType(LocalTestInterface.class) - List l = new ArrayList<>(); - } - class B { - @ElementType(LocalTestAbstractClass.class) - List l = new ArrayList<>(); - } - class C { - @ElementType(int.class) - List l = new ArrayList<>(); - } - class D { - @ElementType(TestSubClass[].class) - List l = new ArrayList<>(); - } - class E { - @ElementType(LocalTestEnum.class) - List l = new ArrayList<>(); - } - Map m = mapOf("l", listOf()); - - String msg = "The element type of field 'l' must be a concrete class " + - "but type 'LocalTestInterface' is an interface."; - assertItmCfgExceptionMessage(new A(), msg); - assertIfmCfgExceptionMessage(new A(), m, msg); - - msg = "The element type of field 'l' must be a concrete class " + - "but type 'LocalTestAbstractClass' is an abstract class."; - assertItmCfgExceptionMessage(new B(), msg); - assertIfmCfgExceptionMessage(new B(), m, msg); - - msg = "The element type 'int' of field 'l' is not a configuration element."; - assertItmCfgExceptionMessage(new C(), msg); - assertIfmCfgExceptionMessage(new C(), m, msg); - - msg = "The element type 'TestSubClass[]' of field 'l' is " + - "not a configuration element."; - assertItmCfgExceptionMessage(new D(), msg); - assertIfmCfgExceptionMessage(new D(), m, msg); - } - - @Test - void instanceToMapRequiresConfigurationElementsToHaveNoArgsConstructors() { - @ConfigurationElement - class Sub { - Sub(int n) {} - } - - class A { - Sub s = new Sub(2); - } - - String msg = "Type 'Sub' of field 's' doesn't have a no-args constructor."; - assertItmCfgExceptionMessage(new A(), msg); - } - - @Test - void instanceToMapRequiresElementTypesToBeConfigurationElements() { - class A { - @ElementType(String.class) - List l = new ArrayList<>(); - } - String msg = "The element type 'String' of field 'l' is not a " + - "configuration element."; - assertItmCfgExceptionMessage(new A(), msg); - } - - @Test - void instanceToMapRequiresElementTypesToHaveNoArgsConstructors() { - class A { - @ElementType(Sub3.class) - List list = new ArrayList<>(); - } - String msg = "The element type 'Sub3' of field 'list' " + - "doesn't have a no-args constructor."; - assertItmCfgExceptionMessage(new A(), msg); - } - - @Test - void instanceToAndFromMapRequireFieldsWithElementTypeToBeContainers() { - class A { - @ElementType(String.class) - String s = ""; - } - String msg = "Field 's' is annotated with the ElementType annotation but " + - "is not a List, Set or Map."; - assertItmCfgExceptionMessage(new A(), msg); - assertIfmCfgExceptionMessage(new A(), mapOf("s", ""), msg); - } - - @Test - void instanceFromMapsRequiresElementTypeToBeEnumType() { - class A { - @ElementType(value = TestSubClass.class, nestingLevel = 1) - List> l = listOf(); - } - Map map = mapOf( - "l", listOf(listOf("Q", "V")) - ); - ConfigurationException ex = assertIfmThrowsCfgException(new A(), map); - Throwable cause = ex.getCause(); - - String msg = "Element type 'TestSubClass' of field 'l' is not an enum type."; - assertThat(cause.getMessage(), is(msg)); - } - - @Test - void instanceFromMapElementConverterRequiresObjectsOfTypeMapStringObject() { - class A { - @ElementType(value = TestSubClass.class, nestingLevel = 1) - List> l = listOf(); - } - Map map = mapOf( - "l", listOf(listOf(1, 2)) - ); - String msg = "Field 'l' of class 'A' has a nesting level of 1 but element " + - "'1' of type 'Integer' cannot be converted to 'TestSubClass'."; - assertIfmCfgExceptionMessage(new A(), map, msg); - } - - @Test - void instanceToMapRequiresCorrectNestingLevelForLists() { - TestSubClass testValues = TestSubClass.TEST_VALUES; - class A { - @ElementType(TestSubClass.class) - List> l1 = listOf(); - - @ElementType(TestSubClass.class) - List> l2 = listOf(listOf(testValues)); - } - class B { - @ElementType(value = TestSubClass.class, nestingLevel = 1) - List>> l = listOf(listOf(listOf(testValues))); - } - class C { - @ElementType(value = TestSubClass.class, nestingLevel = 3) - List>> l = listOf(listOf(listOf(testValues))); - } - class D { - @ElementType(value = TestSubClass.class, nestingLevel = 1) - List> l = listOf(listOf( - TestSubClass.of(11, "11"), TestSubClass.of(12, "12"), - TestSubClass.of(13, "13"), TestSubClass.of(14, "14") - )); - } - - String msg = "Field 'l2' of class 'A' has a nesting level of 0 but the " + - "first object of type 'TestSubClass' was found on level 1."; - assertItmCfgExceptionMessage(new A(), msg); - - msg = "Field 'l' of class 'B' has a nesting level of 1 but the " + - "first object of type 'TestSubClass' was found on level 2."; - assertItmCfgExceptionMessage(new B(), msg); - - msg = "Field 'l' of class 'C' has a nesting level of 3 but the " + - "first object of type 'TestSubClass' was found on level 2."; - assertItmCfgExceptionMessage(new C(), msg); - - Map map = instanceToMap(new D()); - D d = instanceFromMap(new D(), map); - assertThat(d.l, is(new D().l)); - } - - - @Test - void instanceToMapRequiresCorrectNestingLevelForMaps() { - TestSubClass testValues = TestSubClass.TEST_VALUES; - class A { - @ElementType(TestSubClass.class) - Map> m1 = mapOf(); - - @ElementType(TestSubClass.class) - Map> m2 = mapOf( - 1, mapOf("1", TestSubClass.of(11, "11")), - 2, mapOf("2", TestSubClass.of(12, "12")) - ); - } - class B { - @ElementType(value = TestSubClass.class, nestingLevel = 1) - Map>> m = mapOf( - "1", mapOf("2", mapOf("3", testValues)), - "1", mapOf("2", mapOf("3", testValues)) - ); - } - class C { - @ElementType(value = TestSubClass.class, nestingLevel = 3) - Map>> m = mapOf( - "1", mapOf("2", mapOf("3", testValues)), - "1", mapOf("2", mapOf("3", testValues)) - ); - } - class D { - @ElementType(value = TestSubClass.class, nestingLevel = 1) - Map> m = mapOf( - 1, mapOf("1", TestSubClass.of(11, "11")), - 2, mapOf("2", TestSubClass.of(12, "12")), - 3, mapOf("3", TestSubClass.of(13, "13")), - 4, mapOf("4", TestSubClass.of(14, "14")) - ); - } - - String msg = "Field 'm2' of class 'A' has a nesting level of 0 but the " + - "first object of type 'TestSubClass' was found on level 1."; - assertItmCfgExceptionMessage(new A(), msg); - - msg = "Field 'm' of class 'B' has a nesting level of 1 but the " + - "first object of type 'TestSubClass' was found on level 2."; - assertItmCfgExceptionMessage(new B(), msg); - - msg = "Field 'm' of class 'C' has a nesting level of 3 but the " + - "first object of type 'TestSubClass' was found on level 2."; - assertItmCfgExceptionMessage(new C(), msg); - - Map map = instanceToMap(new D()); - D d = instanceFromMap(new D(), map); - assertThat(d.m, is(new D().m)); - } - - /* The case that the nestingLevel is set to high cannot properly be detected. */ - @Test - void instanceFromMapRequiresCorrectNestingLevelForLists() { - class A { - @ElementType(TestSubClass.class) - List l = listOf(); - } - class B { - @ElementType(LocalTestEnum.class) - List l = listOf(); - } - class C { - @ElementType(TestSubClass.class) - List> l = listOf(); - } - class D { - @ElementType(LocalTestEnum.class) - List> l = listOf(); - } - Map m = TestSubClass.TEST_VALUES.asMap(); - instanceFromMap(new A(), mapOf("l", listOf(m))); - instanceFromMap(new B(), mapOf("l", listOf("S", "T"))); - - String elementAsString = m.toString(); - String msg = "Field 'l' of class 'C' has a nesting level of 0 but element '[" + - elementAsString + "]' of type 'ArrayList' cannot be converted " + - "to 'TestSubClass'."; - assertIfmCfgExceptionMessage(new C(), mapOf("l", listOf(listOf(m))), msg); - - msg = "Field 'l' of class 'D' has a nesting level of 0 but element '[S, T]' of type " + - "'ArrayList' cannot be converted to 'LocalTestEnum'."; - assertIfmCfgExceptionMessage(new D(), mapOf("l", listOf(listOf("S", "T"))), msg); - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/ClassWithFinalStaticTransientField.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/ClassWithFinalStaticTransientField.java deleted file mode 100644 index 20e206d..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/ClassWithFinalStaticTransientField.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.exlll.configlib.classes; - -public final class ClassWithFinalStaticTransientField { - private final int i = 0; - private static int j; - private transient int k; -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestClass.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestClass.java deleted file mode 100644 index a215829..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestClass.java +++ /dev/null @@ -1,482 +0,0 @@ -package de.exlll.configlib.classes; - -import de.exlll.configlib.annotation.Comment; -import de.exlll.configlib.annotation.Convert; -import de.exlll.configlib.annotation.ElementType; -import de.exlll.configlib.annotation.NoConvert; -import de.exlll.configlib.configs.yaml.YamlConfiguration; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.*; - -import static de.exlll.configlib.util.CollectionFactory.*; - -@SuppressWarnings("FieldCanBeLocal") -@Comment({"A", "", "B", "C"}) -public final class TestClass extends YamlConfiguration { - public static final TestClass TEST_VALUES; - - public enum TestEnum { - DEFAULT, NON_DEFAULT - } - - static { - TEST_VALUES = new TestClass(); - TEST_VALUES.primBool = true; - TEST_VALUES.refBool = true; - TEST_VALUES.primByte = 1; - TEST_VALUES.refByte = 2; - TEST_VALUES.primChar = 'c'; - TEST_VALUES.refChar = 'd'; - TEST_VALUES.primShort = 3; - TEST_VALUES.refShort = 4; - TEST_VALUES.primInt = 5; - TEST_VALUES.refInt = 6; - TEST_VALUES.primLong = 7; - TEST_VALUES.refLong = 8L; - TEST_VALUES.primFloat = 9.0f; - TEST_VALUES.refFloat = 10.0f; - TEST_VALUES.primDouble = 11.0; - TEST_VALUES.refDouble = 12.0; - TEST_VALUES.string = "string"; - /* other types */ - TEST_VALUES.subClass = TestSubClass.TEST_VALUES; - /* containers of simple types */ - TEST_VALUES.ints = setOf(1, 2, 3); - TEST_VALUES.strings = listOf("a", "b", "c"); - TEST_VALUES.doubleByBool = mapOf(true, 1.0, false, 2.0); - /* containers of other types */ - TEST_VALUES.subClassSet = setOf( - TestSubClass.of(1, "1"), - TestSubClass.of(2, "2") - ); - TEST_VALUES.subClassList = listOf( - TestSubClass.of(3, "3"), - TestSubClass.of(4, "4") - ); - TEST_VALUES.subClassMap = mapOf( - "5", TestSubClass.of(5, "5"), - "6", TestSubClass.of(6, "6") - ); - /* nested containers of simple types */ - TEST_VALUES.listsList = listOf( - listOf(1, 2), listOf(3, 4) - ); - TEST_VALUES.setsSet = setOf( - setOf("a", "b"), setOf("c", "d") - ); - TEST_VALUES.mapsMap = mapOf( - 1, mapOf("1", 1), 2, mapOf("2", 2) - ); - /* nested containers of custom types */ - TEST_VALUES.subClassListsList = listOf( - listOf(TestSubClass.of(7, "7"), TestSubClass.of(8, "8")) - ); - TEST_VALUES.subClassSetsSet = setOf(setOf( - TestSubClass.of(9, "9"), TestSubClass.of(10, "10") - )); - TEST_VALUES.subClassMapsMap = mapOf( - 1, mapOf("1", TestSubClass.of(11, "11")), - 2, mapOf("2", TestSubClass.of(12, "12")) - ); - TEST_VALUES.e1 = TestEnum.NON_DEFAULT; - TEST_VALUES.enums = listOf(TestEnum.DEFAULT, TestEnum.NON_DEFAULT); - TEST_VALUES.converterSubClass = TestSubClass.of(13, "13"); - TEST_VALUES.excludedClass = TestExcludedClass.TEST_VALUES; - } - - /* not converted */ - private static final int staticFinalInt = 1; - private static int staticInt = 2; - private final int finalInt = 3; - private transient int transientInt = 4; - /* simple types */ - @Comment({"A"}) - private boolean primBool; - @Comment({"B", "C"}) - private Boolean refBool = false; - @Comment({"D", "", "E"}) - private byte primByte; - private Byte refByte = 0; - @Comment("F") - private char primChar; - @Comment({"", "G"}) - private Character refChar = '\0'; - private short primShort; - private Short refShort = 0; - private int primInt; - private Integer refInt = 0; - private long primLong; - private Long refLong = 0L; - private float primFloat; - private Float refFloat = 0F; - private double primDouble; - private Double refDouble = 0.0; - private String string = ""; - /* other types */ - private TestSubClass subClass = new TestSubClass(); - /* containers of simple types */ - private Set ints = new HashSet<>(); - private List strings = new ArrayList<>(); - private Map doubleByBool = new HashMap<>(); - /* containers of other types */ - @ElementType(TestSubClass.class) - private Set subClassSet = new HashSet<>(); - @ElementType(TestSubClass.class) - private List subClassList = new ArrayList<>(); - @ElementType(TestSubClass.class) - private Map subClassMap = new HashMap<>(); - /* nested containers of simple types */ - private List> listsList = new ArrayList<>(); - private Set> setsSet = new HashSet<>(); - private Map> mapsMap = new HashMap<>(); - /* nested containers of custom types */ - @ElementType(value = TestSubClass.class, nestingLevel = 1) - private List> subClassListsList = new ArrayList<>(); - @ElementType(value = TestSubClass.class, nestingLevel = 1) - private Set> subClassSetsSet = new HashSet<>(); - @ElementType(value = TestSubClass.class, nestingLevel = 1) - private Map> subClassMapsMap - = new HashMap<>(); - private TestEnum e1 = TestEnum.DEFAULT; - @ElementType(TestEnum.class) - private List enums = new ArrayList<>(); - @Convert(TestSubClassConverter.class) - private TestSubClass converterSubClass = new TestSubClass(); - @NoConvert - private TestExcludedClass excludedClass = new TestExcludedClass(); - - public TestClass(Path path, YamlProperties properties) { - super(path, properties); - } - - public TestClass(Path path) { - super(path); - } - - public TestClass() { - this(Paths.get(""), YamlProperties.DEFAULT); - } - - public TestClass(Path configPath, TestClass other) { - this(configPath); - this.transientInt = other.transientInt; - this.primBool = other.primBool; - this.refBool = other.refBool; - this.primByte = other.primByte; - this.refByte = other.refByte; - this.primChar = other.primChar; - this.refChar = other.refChar; - this.primShort = other.primShort; - this.refShort = other.refShort; - this.primInt = other.primInt; - this.refInt = other.refInt; - this.primLong = other.primLong; - this.refLong = other.refLong; - this.primFloat = other.primFloat; - this.refFloat = other.refFloat; - this.primDouble = other.primDouble; - this.refDouble = other.refDouble; - this.string = other.string; - this.subClass = other.subClass; - this.ints = other.ints; - this.strings = other.strings; - this.doubleByBool = other.doubleByBool; - this.subClassSet = other.subClassSet; - this.subClassList = other.subClassList; - this.subClassMap = other.subClassMap; - this.listsList = other.listsList; - this.setsSet = other.setsSet; - this.mapsMap = other.mapsMap; - this.subClassListsList = other.subClassListsList; - this.subClassSetsSet = other.subClassSetsSet; - this.subClassMapsMap = other.subClassMapsMap; - this.e1 = other.e1; - this.enums = other.enums; - this.converterSubClass = other.converterSubClass; - this.excludedClass = other.excludedClass; - } - - public static int getStaticFinalInt() { - return staticFinalInt; - } - - public static int getStaticInt() { - return staticInt; - } - - public int getFinalInt() { - return finalInt; - } - - public int getTransientInt() { - return transientInt; - } - - public boolean getPrimBool() { - return primBool; - } - - public Boolean getRefBool() { - return refBool; - } - - public byte getPrimByte() { - return primByte; - } - - public Byte getRefByte() { - return refByte; - } - - public char getPrimChar() { - return primChar; - } - - public Character getRefChar() { - return refChar; - } - - public short getPrimShort() { - return primShort; - } - - public Short getRefShort() { - return refShort; - } - - public int getPrimInt() { - return primInt; - } - - public Integer getRefInt() { - return refInt; - } - - public long getPrimLong() { - return primLong; - } - - public Long getRefLong() { - return refLong; - } - - public float getPrimFloat() { - return primFloat; - } - - public Float getRefFloat() { - return refFloat; - } - - public double getPrimDouble() { - return primDouble; - } - - public Double getRefDouble() { - return refDouble; - } - - public String getString() { - return string; - } - - public TestSubClass getSubClass() { - return subClass; - } - - public Set getInts() { - return ints; - } - - public List getStrings() { - return strings; - } - - public Map getDoubleByBool() { - return doubleByBool; - } - - public Set getSubClassSet() { - return subClassSet; - } - - public List getSubClassList() { - return subClassList; - } - - public Map getSubClassMap() { - return subClassMap; - } - - public List> getListsList() { - return listsList; - } - - public Set> getSetsSet() { - return setsSet; - } - - public Map> getMapsMap() { - return mapsMap; - } - - public List> getSubClassListsList() { - return subClassListsList; - } - - public Set> getSubClassSetsSet() { - return subClassSetsSet; - } - - public Map> getSubClassMapsMap() { - return subClassMapsMap; - } - - public TestEnum getE1() { - return e1; - } - - public List getEnums() { - return enums; - } - - public TestSubClass getConverterSubClass() { - return converterSubClass; - } - - public TestExcludedClass getExcludedClass() { - return excludedClass; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof TestClass)) return false; - - TestClass testClass = (TestClass) o; - - if (finalInt != testClass.finalInt) return false; - if (transientInt != testClass.transientInt) return false; - if (primBool != testClass.primBool) return false; - if (primByte != testClass.primByte) return false; - if (primChar != testClass.primChar) return false; - if (primShort != testClass.primShort) return false; - if (primInt != testClass.primInt) return false; - if (primLong != testClass.primLong) return false; - if (Float.compare(testClass.primFloat, primFloat) != 0) return false; - if (Double.compare(testClass.primDouble, primDouble) != 0) return false; - if (!refBool.equals(testClass.refBool)) return false; - if (!refByte.equals(testClass.refByte)) return false; - if (!refChar.equals(testClass.refChar)) return false; - if (!refShort.equals(testClass.refShort)) return false; - if (!refInt.equals(testClass.refInt)) return false; - if (!refLong.equals(testClass.refLong)) return false; - if (!refFloat.equals(testClass.refFloat)) return false; - if (!refDouble.equals(testClass.refDouble)) return false; - if (!string.equals(testClass.string)) return false; - if (!subClass.equals(testClass.subClass)) return false; - if (!ints.equals(testClass.ints)) return false; - if (!strings.equals(testClass.strings)) return false; - if (!doubleByBool.equals(testClass.doubleByBool)) return false; - if (!subClassSet.equals(testClass.subClassSet)) return false; - if (!subClassList.equals(testClass.subClassList)) return false; - if (!subClassMap.equals(testClass.subClassMap)) return false; - if (!listsList.equals(testClass.listsList)) return false; - if (!setsSet.equals(testClass.setsSet)) return false; - if (!mapsMap.equals(testClass.mapsMap)) return false; - if (!subClassListsList.equals(testClass.subClassListsList)) return false; - if (!subClassSetsSet.equals(testClass.subClassSetsSet)) return false; - if (e1 != testClass.e1) return false; - if (!enums.equals(testClass.enums)) return false; - if (!converterSubClass.equals(testClass.converterSubClass)) return false; - if (!excludedClass.equals(testClass.excludedClass)) return false; - return subClassMapsMap.equals(testClass.subClassMapsMap); - } - - @Override - public int hashCode() { - int result; - long temp; - result = finalInt; - result = 31 * result + transientInt; - result = 31 * result + (primBool ? 1 : 0); - result = 31 * result + refBool.hashCode(); - result = 31 * result + (int) primByte; - result = 31 * result + refByte.hashCode(); - result = 31 * result + (int) primChar; - result = 31 * result + refChar.hashCode(); - result = 31 * result + (int) primShort; - result = 31 * result + refShort.hashCode(); - result = 31 * result + primInt; - result = 31 * result + refInt.hashCode(); - result = 31 * result + (int) (primLong ^ (primLong >>> 32)); - result = 31 * result + refLong.hashCode(); - result = 31 * result + (primFloat != +0.0f ? Float.floatToIntBits( - primFloat) : 0); - result = 31 * result + refFloat.hashCode(); - temp = Double.doubleToLongBits(primDouble); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + refDouble.hashCode(); - result = 31 * result + string.hashCode(); - result = 31 * result + subClass.hashCode(); - result = 31 * result + ints.hashCode(); - result = 31 * result + strings.hashCode(); - result = 31 * result + doubleByBool.hashCode(); - result = 31 * result + subClassSet.hashCode(); - result = 31 * result + subClassList.hashCode(); - result = 31 * result + subClassMap.hashCode(); - result = 31 * result + listsList.hashCode(); - result = 31 * result + setsSet.hashCode(); - result = 31 * result + mapsMap.hashCode(); - result = 31 * result + subClassListsList.hashCode(); - result = 31 * result + subClassSetsSet.hashCode(); - result = 31 * result + subClassMapsMap.hashCode(); - result = 31 * result + e1.hashCode(); - result = 31 * result + enums.hashCode(); - result = 31 * result + converterSubClass.hashCode(); - result = 31 * result + excludedClass.hashCode(); - return result; - } - - @Override - public String toString() { - return "TestClass{" + - "\nprimBool=" + primBool + - ",\nrefBool=" + refBool + - ",\nprimByte=" + primByte + - ",\nrefByte=" + refByte + - ",\nprimChar=" + primChar + - ",\nrefChar=" + refChar + - ",\nprimShort=" + primShort + - ",\nrefShort=" + refShort + - ",\nprimInt=" + primInt + - ",\nrefInt=" + refInt + - ",\nprimLong=" + primLong + - ",\nrefLong=" + refLong + - ",\nprimFloat=" + primFloat + - ",\nrefFloat=" + refFloat + - ",\nprimDouble=" + primDouble + - ",\nrefDouble=" + refDouble + - ",\nstring='" + string + '\'' + - ",\nsubClass=" + subClass + - ",\nints=" + ints + - ",\nstrings=" + strings + - ",\ndoubleByBool=" + doubleByBool + - ",\nsubClassSet=" + subClassSet + - ",\nsubClassList=" + subClassList + - ",\nsubClassMap=" + subClassMap + - ",\nlistsList=" + listsList + - ",\nsetsSet=" + setsSet + - ",\nmapsMap=" + mapsMap + - ",\nsubClassListsList=" + subClassListsList + - ",\nsubClassSetsSet=" + subClassSetsSet + - ",\nsubClassMapsMap=" + subClassMapsMap + - ",\ne1=" + e1 + - ",\nenums=" + enums + - ",\nconverterSubClass=" + converterSubClass + - ",\nexcludedClass=" + excludedClass + - '}'; - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestExcludedClass.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestExcludedClass.java deleted file mode 100644 index 077eea8..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestExcludedClass.java +++ /dev/null @@ -1,55 +0,0 @@ -package de.exlll.configlib.classes; - -public class TestExcludedClass { - public static final TestExcludedClass TEST_VALUES; - private int primInt; - private String string = ""; - - static { - TEST_VALUES = new TestExcludedClass(); - TEST_VALUES.primInt = 1; - TEST_VALUES.string = "string"; - } - - public int getPrimInt() { - return primInt; - } - - public String getString() { - return string; - } - - public void setPrimInt(int primInt) { - this.primInt = primInt; - } - - public void setString(String string) { - this.string = string; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - TestExcludedClass that = (TestExcludedClass) o; - - if (primInt != that.primInt) return false; - return string.equals(that.string); - } - - @Override - public int hashCode() { - int result = primInt; - result = 31 * result + string.hashCode(); - return result; - } - - @Override - public String toString() { - return "TestExcludedClass{" + - "primInt=" + primInt + - ", string='" + string + '\'' + - '}'; - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubClass.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubClass.java deleted file mode 100644 index 1f5012b..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubClass.java +++ /dev/null @@ -1,149 +0,0 @@ -package de.exlll.configlib.classes; - -import de.exlll.configlib.annotation.ConfigurationElement; -import de.exlll.configlib.annotation.ElementType; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static de.exlll.configlib.util.CollectionFactory.*; -import static java.util.stream.Collectors.toList; -import static java.util.stream.Collectors.toMap; -import static java.util.stream.Collectors.toSet; - -@SuppressWarnings("FieldCanBeLocal") -@ConfigurationElement -public final class TestSubClass { - public static final TestSubClass TEST_VALUES; - - static { - TEST_VALUES = new TestSubClass(); - TEST_VALUES.primInt = 1; - TEST_VALUES.string = "string"; - TEST_VALUES.list = listOf("list"); - TEST_VALUES.set = setOf("set"); - TEST_VALUES.map = mapOf("map", 1); - TEST_VALUES.testSubSubClass = TestSubSubClass.of(14, "14"); - TEST_VALUES.subClassList = listOf( - TestSubSubClass.of(15, "15"), TestSubSubClass.of(16, "16") - ); - TEST_VALUES.subClassSet = setOf( - TestSubSubClass.of(17, "17"), TestSubSubClass.of(18, "18") - ); - TEST_VALUES.subClassMap = mapOf("map", TestSubSubClass.of(19, "19")); - } - - private final int finalInt = 1; - private int primInt; - private String string = ""; - private List list = Collections.emptyList(); - private Set set = Collections.emptySet(); - private Map map = Collections.emptyMap(); - private TestSubSubClass testSubSubClass = new TestSubSubClass(); - @ElementType(TestSubSubClass.class) - private List subClassList = Collections.emptyList(); - @ElementType(TestSubSubClass.class) - private Set subClassSet = Collections.emptySet(); - @ElementType(TestSubSubClass.class) - private Map subClassMap = Collections.emptyMap(); - - public static TestSubClass of(int primInt, String string) { - TestSubClass cls = new TestSubClass(); - cls.primInt = primInt; - cls.string = string; - cls.list = listOf(string); - cls.set = setOf(string); - cls.map = mapOf(string, primInt); - cls.testSubSubClass = TestSubSubClass.of(primInt, string); - cls.subClassList = listOf( - TestSubSubClass.of(primInt * 100, string) - ); - cls.subClassSet = setOf( - TestSubSubClass.of(primInt * 101, string) - ); - cls.subClassMap = mapOf(string, TestSubSubClass.of(primInt * 102, string)); - return cls; - } - - public Map asMap() { - Map asMap = mapOf("primInt", primInt, "string", string); - asMap.put("list", list); - asMap.put("set", set); - asMap.put("map", map); - asMap.put("testSubSubClass", testSubSubClass.asMap()); - asMap.put( - "subClassList", subClassList.stream() - .map(TestSubSubClass::asMap) - .collect(toList()) - ); - asMap.put( - "subClassSet", subClassSet.stream() - .map(TestSubSubClass::asMap) - .collect(toSet()) - ); - asMap.put( - "subClassMap", subClassMap.entrySet().stream() - .map(e -> mapEntry(e.getKey(), e.getValue().asMap())) - .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)) - ); - return asMap; - } - - public int getPrimInt() { - return primInt; - } - - public String getString() { - return string; - } - - @Override - public String toString() { - return "TestSubClass{" + - "primInt=" + primInt + - ", string='" + string + '\'' + - ", list=" + list + - ", set=" + set + - ", map=" + map + - ", testSubSubClass=" + testSubSubClass + - ", subClassList=" + subClassList + - ", subClassSet=" + subClassSet + - ", subClassMap=" + subClassMap + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - TestSubClass that = (TestSubClass) o; - - if (primInt != that.primInt) return false; - if (!string.equals(that.string)) return false; - if (!list.equals(that.list)) return false; - if (!set.equals(that.set)) return false; - if (!map.equals(that.map)) return false; - if (!testSubSubClass.equals(that.testSubSubClass)) return false; - if (!subClassList.equals(that.subClassList)) return false; - if (!subClassSet.equals(that.subClassSet)) return false; - return subClassMap.equals(that.subClassMap); - } - - @Override - public int hashCode() { - int result = finalInt; - result = 31 * result + primInt; - result = 31 * result + string.hashCode(); - result = 31 * result + list.hashCode(); - result = 31 * result + set.hashCode(); - result = 31 * result + map.hashCode(); - result = 31 * result + testSubSubClass.hashCode(); - result = 31 * result + subClassList.hashCode(); - result = 31 * result + subClassSet.hashCode(); - result = 31 * result + subClassMap.hashCode(); - return result; - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubClassConverter.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubClassConverter.java deleted file mode 100644 index 571598b..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubClassConverter.java +++ /dev/null @@ -1,23 +0,0 @@ -package de.exlll.configlib.classes; - -import de.exlll.configlib.Converter; - -public final class TestSubClassConverter - implements Converter { - - @Override - public String convertTo(TestSubClass element, ConversionInfo info) { - return element.getPrimInt() + ":" + element.getString(); - } - - @Override - public TestSubClass convertFrom(String element, ConversionInfo info) { - String[] split = element.split(":"); - return TestSubClass.of(Integer.parseInt(split[0]), split[1]); - } - - @Override - public String toString() { - return "TestSubClassConverter"; - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubSubClass.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubSubClass.java deleted file mode 100644 index b5475f8..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/classes/TestSubSubClass.java +++ /dev/null @@ -1,83 +0,0 @@ -package de.exlll.configlib.classes; - -import de.exlll.configlib.annotation.ConfigurationElement; - -import java.util.*; - -import static de.exlll.configlib.util.CollectionFactory.listOf; -import static de.exlll.configlib.util.CollectionFactory.mapOf; -import static de.exlll.configlib.util.CollectionFactory.setOf; - -@ConfigurationElement -public final class TestSubSubClass { - public static final TestSubSubClass TEST_VALUES; - - static { - TEST_VALUES = new TestSubSubClass(); - TEST_VALUES.primInt = 1; - TEST_VALUES.string = "string"; - TEST_VALUES.list = listOf("list"); - TEST_VALUES.set = setOf("set"); - TEST_VALUES.map = mapOf("map", 1); - } - - private int primInt; - private String string = ""; - private List list = listOf(); - private Set set = setOf(); - private Map map = mapOf(); - - public static TestSubSubClass of(int primInt, String string) { - TestSubSubClass cls = new TestSubSubClass(); - cls.primInt = primInt; - cls.string = string; - String concat = string + string; - cls.list = listOf(concat); - cls.set = setOf(concat); - cls.map = mapOf(concat, primInt); - return cls; - } - - public Map asMap() { - Map asMap = mapOf("primInt", primInt, "string", string); - asMap.put("list", list); - asMap.put("set", set); - asMap.put("map", map); - return asMap; - } - - @Override - public String toString() { - return "TestSubSubClass{" + - "primInt=" + primInt + - ", string='" + string + '\'' + - ", list=" + list + - ", set=" + set + - ", map=" + map + - '}'; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - TestSubSubClass that = (TestSubSubClass) o; - - if (primInt != that.primInt) return false; - if (!string.equals(that.string)) return false; - if (!list.equals(that.list)) return false; - if (!set.equals(that.set)) return false; - return map.equals(that.map); - } - - @Override - public int hashCode() { - int result = primInt; - result = 31 * result + string.hashCode(); - result = 31 * result + list.hashCode(); - result = 31 * result + set.hashCode(); - result = 31 * result + map.hashCode(); - return result; - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/configs/mem/InSharedMemoryConfiguration.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/configs/mem/InSharedMemoryConfiguration.java deleted file mode 100644 index 2ab4123..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/configs/mem/InSharedMemoryConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -package de.exlll.configlib.configs.mem; - -import de.exlll.configlib.Configuration; -import de.exlll.configlib.ConfigurationSource; - -import java.util.Map; -import java.util.Objects; - -public class InSharedMemoryConfiguration - extends Configuration { - private final InSharedMemorySource source = new InSharedMemorySource(); - - protected InSharedMemoryConfiguration(Properties properties) { - super(properties); - } - - @Override - protected ConfigurationSource getSource() { - return source; - } - - @Override - protected InSharedMemoryConfiguration getThis() { - return this; - } - - private static final class InSharedMemorySource implements - ConfigurationSource { - private static Map map; - - @Override - public Map loadConfiguration( - InSharedMemoryConfiguration config - ) { - return Objects.requireNonNull(map); - } - - @Override - public void saveConfiguration( - InSharedMemoryConfiguration config, Map map - ) { - InSharedMemorySource.map = map; - } - } -} diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/configs/yaml/YamlConfigurationTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/configs/yaml/YamlConfigurationTest.java deleted file mode 100644 index f4a8c39..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/configs/yaml/YamlConfigurationTest.java +++ /dev/null @@ -1,941 +0,0 @@ -package de.exlll.configlib.configs.yaml; - -import com.google.common.jimfs.Jimfs; -import de.exlll.configlib.Configuration; -import de.exlll.configlib.annotation.Comment; -import de.exlll.configlib.classes.TestClass; -import de.exlll.configlib.configs.yaml.YamlConfiguration.YamlProperties; -import de.exlll.configlib.format.FieldNameFormatters; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.Files; -import java.nio.file.Path; - -import static de.exlll.configlib.util.CollectionFactory.listOf; -import static java.util.stream.Collectors.joining; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertThat; -import static org.junit.jupiter.api.Assertions.assertThrows; - -@SuppressWarnings("unused") -class YamlConfigurationTest { - private FileSystem fileSystem; - private Path testPath, configPath; - - @BeforeEach - void setUp() { - fileSystem = Jimfs.newFileSystem(); - testPath = fileSystem.getPath("/a/b/test.yml"); - configPath = fileSystem.getPath("/a/b/config.yml"); - } - - @AfterEach - void tearDown() throws IOException { - fileSystem.close(); - } - - @Test - void loadAndSaveExecutesPostLoadHook() { - class A extends YamlConfiguration { - int i = 0; - - protected A() { super(configPath, YamlProperties.DEFAULT); } - - @Override - protected void postLoad() { - i++; - } - } - - A a = new A(); - a.loadAndSave(); - assertThat(a.i, is(1)); - } - - @Test - void loadAndSaveSavesConfiguration() { - YamlConfiguration configuration = new TestClass( - configPath, TestClass.TEST_VALUES - ); - configuration.loadAndSave(); - assertThat(Files.exists(configPath), is(true)); - - YamlConfiguration load = new TestClass(configPath); - load.load(); - assertThat(load, is(TestClass.TEST_VALUES)); - } - - @Test - void loadAndSaveLoadsConfiguration() { - new TestClass(configPath, TestClass.TEST_VALUES).save(); - - YamlConfiguration configuration = new TestClass(configPath); - configuration.loadAndSave(); - assertThat(configuration, is(TestClass.TEST_VALUES)); - assertThat(Files.exists(configPath), is(true)); - } - - @Test - void loadLoadsConfig() { - setupConfigPath(); - Configuration configuration = new TestClass(configPath); - assertThat(configuration, is(not(TestClass.TEST_VALUES))); - configuration.load(); - assertThat(configuration, is((TestClass.TEST_VALUES))); - } - - private void setupConfigPath() { - Configuration configuration = new TestClass( - configPath, TestClass.TEST_VALUES - ); - configuration.save(); - } - - @Test - void loadThrowsExceptionIfTypesDoNotMatch() { - Configuration configuration = new TestClass(configPath); - configuration.save(); - assertThrows(IllegalArgumentException.class, configuration::load); - } - - @Test - void saveCreatesConfig() { - assertThat(Files.exists(testPath), is(false)); - Configuration configuration = new TestClass(testPath); - configuration.save(); - assertThat(Files.exists(testPath), is(true)); - } - - @Test - void saveDumpsYaml() throws IOException { - Configuration configuration = new TestClass( - testPath, TestClass.TEST_VALUES - ); - configuration.save(); - assertThat(readConfig(testPath), is(TEST_CLASS_YML)); - } - - @Test - void saveDumpsPrependedAndAppendedComments() throws IOException { - class A extends YamlConfiguration { - int i; - - protected A(YamlProperties properties) { - super(testPath, properties); - } - } - YamlProperties properties = YamlProperties.builder() - .setPrependedComments(listOf("AB", "", "CD")) - .setAppendedComments(listOf("AB", "", "CD")) - .build(); - new A(properties).save(); - assertThat(readConfig(testPath), is(PRE_AND_APPENDED_COMMENTS_YML)); - } - - @Test - void saveDumpsClassComments() throws IOException { - @Comment({"1", "", "2"}) - class A extends YamlConfiguration { - @Comment("a") - private int a = 1; - @Comment({"b", "x"}) - private int b = 2; - @Comment({"c", "", "y"}) - private int c = 3; - private int d = 4; - - protected A() { super(testPath); } - } - new A().save(); - assertThat(readConfig(testPath), is(CLASS_COMMENTS_YML)); - } - - @Test - void saveDumpsFieldComments() throws IOException { - class A extends YamlConfiguration { - @Comment("a") - private int a = 1; - @Comment({"b", "x"}) - private int b = 2; - @Comment({"c", "", "y"}) - private int c = 3; - private int d = 4; - - protected A() { super(testPath); } - } - new A().save(); - assertThat(readConfig(testPath), is(FIELD_COMMENTS_YML)); - } - - @Test - void saveDumpsFormattedFieldComments() throws IOException { - class A extends YamlConfiguration { - @Comment("aB") - private int aB = 1; - @Comment({"cD", "dC"}) - private int cD = 2; - - protected A(YamlProperties properties) { - super(testPath, properties); - } - } - - YamlProperties properties = YamlProperties.builder() - .setFormatter(FieldNameFormatters.LOWER_UNDERSCORE) - .build(); - - new A(properties).save(); - assertThat(readConfig(testPath), is(FORMATTED_FIELD_COMMENTS_YML)); - } - - private String readConfig(Path path) throws IOException { - return Files.lines(path).collect(joining("\n")); - } - - private static final String PRE_AND_APPENDED_COMMENTS_YML = "# AB\n" + - "\n" + - "# CD\n" + - "i: 0\n" + - "# AB\n" + - "\n" + - "# CD"; - - private static final String FIELD_COMMENTS_YML = "# a\n" + - "a: 1\n" + - "# b\n" + - "# x\n" + - "b: 2\n" + - "# c\n" + - "\n" + - "# y\n" + - "c: 3\n" + - "d: 4"; - - private static final String FORMATTED_FIELD_COMMENTS_YML = "# aB\n" + - "a_b: 1\n" + - "# cD\n" + - "# dC\n" + - "c_d: 2"; - - private static final String CLASS_COMMENTS_YML = "# 1\n" + - "\n" + - "# 2\n" + - "# a\n" + - "a: 1\n" + - "# b\n" + - "# x\n" + - "b: 2\n" + - "# c\n" + - "\n" + - "# y\n" + - "c: 3\n" + - "d: 4"; - - private static final String TEST_CLASS_YML = "# A\n" + - "\n" + - "# B\n" + - "# C\n" + - "# A\n" + - "primBool: true\n" + - "# B\n" + - "# C\n" + - "refBool: true\n" + - "# D\n" + - "\n" + - "# E\n" + - "primByte: 1\n" + - "refByte: 2\n" + - "# F\n" + - "primChar: c\n" + - "\n" + - "# G\n" + - "refChar: d\n" + - "primShort: 3\n" + - "refShort: 4\n" + - "primInt: 5\n" + - "refInt: 6\n" + - "primLong: 7\n" + - "refLong: 8\n" + - "primFloat: 9.0\n" + - "refFloat: 10.0\n" + - "primDouble: 11.0\n" + - "refDouble: 12.0\n" + - "string: string\n" + - "subClass:\n" + - " primInt: 1\n" + - " string: string\n" + - " list:\n" + - " - list\n" + - " set: !!set\n" + - " set: null\n" + - " map:\n" + - " map: 1\n" + - " testSubSubClass:\n" + - " primInt: 14\n" + - " string: '14'\n" + - " list:\n" + - " - '1414'\n" + - " set: !!set\n" + - " '1414': null\n" + - " map:\n" + - " '1414': 14\n" + - " subClassList:\n" + - " - primInt: 15\n" + - " string: '15'\n" + - " list:\n" + - " - '1515'\n" + - " set: !!set\n" + - " '1515': null\n" + - " map:\n" + - " '1515': 15\n" + - " - primInt: 16\n" + - " string: '16'\n" + - " list:\n" + - " - '1616'\n" + - " set: !!set\n" + - " '1616': null\n" + - " map:\n" + - " '1616': 16\n" + - " subClassSet: !!set\n" + - " ? primInt: 18\n" + - " string: '18'\n" + - " list:\n" + - " - '1818'\n" + - " set: !!set\n" + - " '1818': null\n" + - " map:\n" + - " '1818': 18\n" + - " : null\n" + - " ? primInt: 17\n" + - " string: '17'\n" + - " list:\n" + - " - '1717'\n" + - " set: !!set\n" + - " '1717': null\n" + - " map:\n" + - " '1717': 17\n" + - " : null\n" + - " subClassMap:\n" + - " map:\n" + - " primInt: 19\n" + - " string: '19'\n" + - " list:\n" + - " - '1919'\n" + - " set: !!set\n" + - " '1919': null\n" + - " map:\n" + - " '1919': 19\n" + - "ints: !!set\n" + - " 1: null\n" + - " 2: null\n" + - " 3: null\n" + - "strings:\n" + - "- a\n" + - "- b\n" + - "- c\n" + - "doubleByBool:\n" + - " true: 1.0\n" + - " false: 2.0\n" + - "subClassSet: !!set\n" + - " ? primInt: 1\n" + - " string: '1'\n" + - " list:\n" + - " - '1'\n" + - " set: !!set\n" + - " '1': null\n" + - " map:\n" + - " '1': 1\n" + - " testSubSubClass:\n" + - " primInt: 1\n" + - " string: '1'\n" + - " list:\n" + - " - '11'\n" + - " set: !!set\n" + - " '11': null\n" + - " map:\n" + - " '11': 1\n" + - " subClassList:\n" + - " - primInt: 100\n" + - " string: '1'\n" + - " list:\n" + - " - '11'\n" + - " set: !!set\n" + - " '11': null\n" + - " map:\n" + - " '11': 100\n" + - " subClassSet: !!set\n" + - " ? primInt: 101\n" + - " string: '1'\n" + - " list:\n" + - " - '11'\n" + - " set: !!set\n" + - " '11': null\n" + - " map:\n" + - " '11': 101\n" + - " : null\n" + - " subClassMap:\n" + - " '1':\n" + - " primInt: 102\n" + - " string: '1'\n" + - " list:\n" + - " - '11'\n" + - " set: !!set\n" + - " '11': null\n" + - " map:\n" + - " '11': 102\n" + - " : null\n" + - " ? primInt: 2\n" + - " string: '2'\n" + - " list:\n" + - " - '2'\n" + - " set: !!set\n" + - " '2': null\n" + - " map:\n" + - " '2': 2\n" + - " testSubSubClass:\n" + - " primInt: 2\n" + - " string: '2'\n" + - " list:\n" + - " - '22'\n" + - " set: !!set\n" + - " '22': null\n" + - " map:\n" + - " '22': 2\n" + - " subClassList:\n" + - " - primInt: 200\n" + - " string: '2'\n" + - " list:\n" + - " - '22'\n" + - " set: !!set\n" + - " '22': null\n" + - " map:\n" + - " '22': 200\n" + - " subClassSet: !!set\n" + - " ? primInt: 202\n" + - " string: '2'\n" + - " list:\n" + - " - '22'\n" + - " set: !!set\n" + - " '22': null\n" + - " map:\n" + - " '22': 202\n" + - " : null\n" + - " subClassMap:\n" + - " '2':\n" + - " primInt: 204\n" + - " string: '2'\n" + - " list:\n" + - " - '22'\n" + - " set: !!set\n" + - " '22': null\n" + - " map:\n" + - " '22': 204\n" + - " : null\n" + - "subClassList:\n" + - "- primInt: 3\n" + - " string: '3'\n" + - " list:\n" + - " - '3'\n" + - " set: !!set\n" + - " '3': null\n" + - " map:\n" + - " '3': 3\n" + - " testSubSubClass:\n" + - " primInt: 3\n" + - " string: '3'\n" + - " list:\n" + - " - '33'\n" + - " set: !!set\n" + - " '33': null\n" + - " map:\n" + - " '33': 3\n" + - " subClassList:\n" + - " - primInt: 300\n" + - " string: '3'\n" + - " list:\n" + - " - '33'\n" + - " set: !!set\n" + - " '33': null\n" + - " map:\n" + - " '33': 300\n" + - " subClassSet: !!set\n" + - " ? primInt: 303\n" + - " string: '3'\n" + - " list:\n" + - " - '33'\n" + - " set: !!set\n" + - " '33': null\n" + - " map:\n" + - " '33': 303\n" + - " : null\n" + - " subClassMap:\n" + - " '3':\n" + - " primInt: 306\n" + - " string: '3'\n" + - " list:\n" + - " - '33'\n" + - " set: !!set\n" + - " '33': null\n" + - " map:\n" + - " '33': 306\n" + - "- primInt: 4\n" + - " string: '4'\n" + - " list:\n" + - " - '4'\n" + - " set: !!set\n" + - " '4': null\n" + - " map:\n" + - " '4': 4\n" + - " testSubSubClass:\n" + - " primInt: 4\n" + - " string: '4'\n" + - " list:\n" + - " - '44'\n" + - " set: !!set\n" + - " '44': null\n" + - " map:\n" + - " '44': 4\n" + - " subClassList:\n" + - " - primInt: 400\n" + - " string: '4'\n" + - " list:\n" + - " - '44'\n" + - " set: !!set\n" + - " '44': null\n" + - " map:\n" + - " '44': 400\n" + - " subClassSet: !!set\n" + - " ? primInt: 404\n" + - " string: '4'\n" + - " list:\n" + - " - '44'\n" + - " set: !!set\n" + - " '44': null\n" + - " map:\n" + - " '44': 404\n" + - " : null\n" + - " subClassMap:\n" + - " '4':\n" + - " primInt: 408\n" + - " string: '4'\n" + - " list:\n" + - " - '44'\n" + - " set: !!set\n" + - " '44': null\n" + - " map:\n" + - " '44': 408\n" + - "subClassMap:\n" + - " '5':\n" + - " primInt: 5\n" + - " string: '5'\n" + - " list:\n" + - " - '5'\n" + - " set: !!set\n" + - " '5': null\n" + - " map:\n" + - " '5': 5\n" + - " testSubSubClass:\n" + - " primInt: 5\n" + - " string: '5'\n" + - " list:\n" + - " - '55'\n" + - " set: !!set\n" + - " '55': null\n" + - " map:\n" + - " '55': 5\n" + - " subClassList:\n" + - " - primInt: 500\n" + - " string: '5'\n" + - " list:\n" + - " - '55'\n" + - " set: !!set\n" + - " '55': null\n" + - " map:\n" + - " '55': 500\n" + - " subClassSet: !!set\n" + - " ? primInt: 505\n" + - " string: '5'\n" + - " list:\n" + - " - '55'\n" + - " set: !!set\n" + - " '55': null\n" + - " map:\n" + - " '55': 505\n" + - " : null\n" + - " subClassMap:\n" + - " '5':\n" + - " primInt: 510\n" + - " string: '5'\n" + - " list:\n" + - " - '55'\n" + - " set: !!set\n" + - " '55': null\n" + - " map:\n" + - " '55': 510\n" + - " '6':\n" + - " primInt: 6\n" + - " string: '6'\n" + - " list:\n" + - " - '6'\n" + - " set: !!set\n" + - " '6': null\n" + - " map:\n" + - " '6': 6\n" + - " testSubSubClass:\n" + - " primInt: 6\n" + - " string: '6'\n" + - " list:\n" + - " - '66'\n" + - " set: !!set\n" + - " '66': null\n" + - " map:\n" + - " '66': 6\n" + - " subClassList:\n" + - " - primInt: 600\n" + - " string: '6'\n" + - " list:\n" + - " - '66'\n" + - " set: !!set\n" + - " '66': null\n" + - " map:\n" + - " '66': 600\n" + - " subClassSet: !!set\n" + - " ? primInt: 606\n" + - " string: '6'\n" + - " list:\n" + - " - '66'\n" + - " set: !!set\n" + - " '66': null\n" + - " map:\n" + - " '66': 606\n" + - " : null\n" + - " subClassMap:\n" + - " '6':\n" + - " primInt: 612\n" + - " string: '6'\n" + - " list:\n" + - " - '66'\n" + - " set: !!set\n" + - " '66': null\n" + - " map:\n" + - " '66': 612\n" + - "listsList:\n" + - "- - 1\n" + - " - 2\n" + - "- - 3\n" + - " - 4\n" + - "setsSet: !!set\n" + - " ? !!set\n" + - " a: null\n" + - " b: null\n" + - " : null\n" + - " ? !!set\n" + - " c: null\n" + - " d: null\n" + - " : null\n" + - "mapsMap:\n" + - " 1:\n" + - " '1': 1\n" + - " 2:\n" + - " '2': 2\n" + - "subClassListsList:\n" + - "- - primInt: 7\n" + - " string: '7'\n" + - " list:\n" + - " - '7'\n" + - " set: !!set\n" + - " '7': null\n" + - " map:\n" + - " '7': 7\n" + - " testSubSubClass:\n" + - " primInt: 7\n" + - " string: '7'\n" + - " list:\n" + - " - '77'\n" + - " set: !!set\n" + - " '77': null\n" + - " map:\n" + - " '77': 7\n" + - " subClassList:\n" + - " - primInt: 700\n" + - " string: '7'\n" + - " list:\n" + - " - '77'\n" + - " set: !!set\n" + - " '77': null\n" + - " map:\n" + - " '77': 700\n" + - " subClassSet: !!set\n" + - " ? primInt: 707\n" + - " string: '7'\n" + - " list:\n" + - " - '77'\n" + - " set: !!set\n" + - " '77': null\n" + - " map:\n" + - " '77': 707\n" + - " : null\n" + - " subClassMap:\n" + - " '7':\n" + - " primInt: 714\n" + - " string: '7'\n" + - " list:\n" + - " - '77'\n" + - " set: !!set\n" + - " '77': null\n" + - " map:\n" + - " '77': 714\n" + - " - primInt: 8\n" + - " string: '8'\n" + - " list:\n" + - " - '8'\n" + - " set: !!set\n" + - " '8': null\n" + - " map:\n" + - " '8': 8\n" + - " testSubSubClass:\n" + - " primInt: 8\n" + - " string: '8'\n" + - " list:\n" + - " - '88'\n" + - " set: !!set\n" + - " '88': null\n" + - " map:\n" + - " '88': 8\n" + - " subClassList:\n" + - " - primInt: 800\n" + - " string: '8'\n" + - " list:\n" + - " - '88'\n" + - " set: !!set\n" + - " '88': null\n" + - " map:\n" + - " '88': 800\n" + - " subClassSet: !!set\n" + - " ? primInt: 808\n" + - " string: '8'\n" + - " list:\n" + - " - '88'\n" + - " set: !!set\n" + - " '88': null\n" + - " map:\n" + - " '88': 808\n" + - " : null\n" + - " subClassMap:\n" + - " '8':\n" + - " primInt: 816\n" + - " string: '8'\n" + - " list:\n" + - " - '88'\n" + - " set: !!set\n" + - " '88': null\n" + - " map:\n" + - " '88': 816\n" + - "subClassSetsSet: !!set\n" + - " ? !!set\n" + - " ? primInt: 10\n" + - " string: '10'\n" + - " list:\n" + - " - '10'\n" + - " set: !!set\n" + - " '10': null\n" + - " map:\n" + - " '10': 10\n" + - " testSubSubClass:\n" + - " primInt: 10\n" + - " string: '10'\n" + - " list:\n" + - " - '1010'\n" + - " set: !!set\n" + - " '1010': null\n" + - " map:\n" + - " '1010': 10\n" + - " subClassList:\n" + - " - primInt: 1000\n" + - " string: '10'\n" + - " list:\n" + - " - '1010'\n" + - " set: !!set\n" + - " '1010': null\n" + - " map:\n" + - " '1010': 1000\n" + - " subClassSet: !!set\n" + - " ? primInt: 1010\n" + - " string: '10'\n" + - " list:\n" + - " - '1010'\n" + - " set: !!set\n" + - " '1010': null\n" + - " map:\n" + - " '1010': 1010\n" + - " : null\n" + - " subClassMap:\n" + - " '10':\n" + - " primInt: 1020\n" + - " string: '10'\n" + - " list:\n" + - " - '1010'\n" + - " set: !!set\n" + - " '1010': null\n" + - " map:\n" + - " '1010': 1020\n" + - " : null\n" + - " ? primInt: 9\n" + - " string: '9'\n" + - " list:\n" + - " - '9'\n" + - " set: !!set\n" + - " '9': null\n" + - " map:\n" + - " '9': 9\n" + - " testSubSubClass:\n" + - " primInt: 9\n" + - " string: '9'\n" + - " list:\n" + - " - '99'\n" + - " set: !!set\n" + - " '99': null\n" + - " map:\n" + - " '99': 9\n" + - " subClassList:\n" + - " - primInt: 900\n" + - " string: '9'\n" + - " list:\n" + - " - '99'\n" + - " set: !!set\n" + - " '99': null\n" + - " map:\n" + - " '99': 900\n" + - " subClassSet: !!set\n" + - " ? primInt: 909\n" + - " string: '9'\n" + - " list:\n" + - " - '99'\n" + - " set: !!set\n" + - " '99': null\n" + - " map:\n" + - " '99': 909\n" + - " : null\n" + - " subClassMap:\n" + - " '9':\n" + - " primInt: 918\n" + - " string: '9'\n" + - " list:\n" + - " - '99'\n" + - " set: !!set\n" + - " '99': null\n" + - " map:\n" + - " '99': 918\n" + - " : null\n" + - " : null\n" + - "subClassMapsMap:\n" + - " 1:\n" + - " '1':\n" + - " primInt: 11\n" + - " string: '11'\n" + - " list:\n" + - " - '11'\n" + - " set: !!set\n" + - " '11': null\n" + - " map:\n" + - " '11': 11\n" + - " testSubSubClass:\n" + - " primInt: 11\n" + - " string: '11'\n" + - " list:\n" + - " - '1111'\n" + - " set: !!set\n" + - " '1111': null\n" + - " map:\n" + - " '1111': 11\n" + - " subClassList:\n" + - " - primInt: 1100\n" + - " string: '11'\n" + - " list:\n" + - " - '1111'\n" + - " set: !!set\n" + - " '1111': null\n" + - " map:\n" + - " '1111': 1100\n" + - " subClassSet: !!set\n" + - " ? primInt: 1111\n" + - " string: '11'\n" + - " list:\n" + - " - '1111'\n" + - " set: !!set\n" + - " '1111': null\n" + - " map:\n" + - " '1111': 1111\n" + - " : null\n" + - " subClassMap:\n" + - " '11':\n" + - " primInt: 1122\n" + - " string: '11'\n" + - " list:\n" + - " - '1111'\n" + - " set: !!set\n" + - " '1111': null\n" + - " map:\n" + - " '1111': 1122\n" + - " 2:\n" + - " '2':\n" + - " primInt: 12\n" + - " string: '12'\n" + - " list:\n" + - " - '12'\n" + - " set: !!set\n" + - " '12': null\n" + - " map:\n" + - " '12': 12\n" + - " testSubSubClass:\n" + - " primInt: 12\n" + - " string: '12'\n" + - " list:\n" + - " - '1212'\n" + - " set: !!set\n" + - " '1212': null\n" + - " map:\n" + - " '1212': 12\n" + - " subClassList:\n" + - " - primInt: 1200\n" + - " string: '12'\n" + - " list:\n" + - " - '1212'\n" + - " set: !!set\n" + - " '1212': null\n" + - " map:\n" + - " '1212': 1200\n" + - " subClassSet: !!set\n" + - " ? primInt: 1212\n" + - " string: '12'\n" + - " list:\n" + - " - '1212'\n" + - " set: !!set\n" + - " '1212': null\n" + - " map:\n" + - " '1212': 1212\n" + - " : null\n" + - " subClassMap:\n" + - " '12':\n" + - " primInt: 1224\n" + - " string: '12'\n" + - " list:\n" + - " - '1212'\n" + - " set: !!set\n" + - " '1212': null\n" + - " map:\n" + - " '1212': 1224\n" + - "e1: NON_DEFAULT\n" + - "enums:\n" + - "- DEFAULT\n" + - "- NON_DEFAULT\n" + - "converterSubClass: '13:13'\n" + - "excludedClass: !!de.exlll.configlib.classes.TestExcludedClass\n" + - " primInt: 1\n" + - " string: string"; -} \ No newline at end of file diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/configs/yaml/YamlSourceTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/configs/yaml/YamlSourceTest.java deleted file mode 100644 index 09ed384..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/configs/yaml/YamlSourceTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package de.exlll.configlib.configs.yaml; - -import com.google.common.jimfs.Jimfs; -import de.exlll.configlib.classes.TestClass; -import de.exlll.configlib.configs.yaml.YamlConfiguration.YamlProperties; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.file.FileSystem; -import java.nio.file.Files; -import java.nio.file.Path; - -import static de.exlll.configlib.util.CollectionFactory.mapOf; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -class YamlSourceTest { - private FileSystem fileSystem; - private Path configPath; - - @BeforeEach - void setUp() { - fileSystem = Jimfs.newFileSystem(); - configPath = fileSystem.getPath("/a/b/config.yml"); - } - - @AfterEach - void tearDown() throws IOException { - fileSystem.close(); - } - - @Test - void yamlSourceCreatesDirectories() throws IOException { - YamlSource source = new YamlSource(configPath, YamlProperties.DEFAULT); - Path parentDir = configPath.getParent(); - assertThat(Files.exists(parentDir), is(false)); - source.saveConfiguration(new TestClass(configPath), mapOf()); - assertThat(Files.exists(parentDir), is(true)); - } -} \ No newline at end of file diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/filter/FieldFilterTest.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/filter/FieldFilterTest.java deleted file mode 100644 index 0516d17..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/filter/FieldFilterTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.exlll.configlib.filter; - -import de.exlll.configlib.classes.ClassWithFinalStaticTransientField; -import org.junit.jupiter.api.Test; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.List; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -class FieldFilterTest { - private static final FieldFilter filter = FieldFilters.DEFAULT; - private static final Class CWFSTF = - ClassWithFinalStaticTransientField.class; - - @Test - void filteredFieldsFiltersFields() throws NoSuchFieldException { - List fields = filter.filterDeclaredFieldsOf(CWFSTF); - assertThat(fields.size(), is(0)); - - class A { - private int i; - private final int j = 0; - private transient int k; - } - fields = filter.filterDeclaredFieldsOf(A.class); - assertThat(fields.size(), is(1)); - assertThat(fields.get(0), is(A.class.getDeclaredField("i"))); - } - - @Test - void defaultFilterFiltersSyntheticFields() { - for (Field field : ClassWithSyntheticField.class.getDeclaredFields()) { - assertThat(field.isSynthetic(), is(true)); - assertThat(filter.test(field), is(false)); - } - } - - @Test - void defaultFilterFiltersFinalStaticTransientFields() - throws NoSuchFieldException { - Field field = CWFSTF.getDeclaredField("i"); - assertThat(Modifier.isFinal(field.getModifiers()), is(true)); - assertThat(filter.test(field), is(false)); - - field = CWFSTF.getDeclaredField("j"); - assertThat(Modifier.isStatic(field.getModifiers()), is(true)); - assertThat(filter.test(field), is(false)); - - field = CWFSTF.getDeclaredField("k"); - assertThat(Modifier.isTransient(field.getModifiers()), is(true)); - assertThat(filter.test(field), is(false)); - } - - private final class ClassWithSyntheticField {} -} \ No newline at end of file diff --git a/ConfigLib-Core/src/test/java/de/exlll/configlib/util/CollectionFactory.java b/ConfigLib-Core/src/test/java/de/exlll/configlib/util/CollectionFactory.java deleted file mode 100644 index 56f9d31..0000000 --- a/ConfigLib-Core/src/test/java/de/exlll/configlib/util/CollectionFactory.java +++ /dev/null @@ -1,87 +0,0 @@ -package de.exlll.configlib.util; - -import java.util.*; - -public final class CollectionFactory { - - private CollectionFactory() { throw new AssertionError(); } - - @SafeVarargs - public static List listOf(T... values) { - return Arrays.asList(values); - } - - @SafeVarargs - public static Set setOf(T... values) { - return new LinkedHashSet<>(Arrays.asList(values)); - } - - public static Map mapOf() { - return new LinkedHashMap<>(); - } - - public static Map mapOf(K k, V v) { - HashMap map = new LinkedHashMap<>(); - map.put(k, v); - return map; - } - - public static Map mapOf(K k1, V v1, K k2, V v2) { - Map map = mapOf(k1, v1); - map.put(k2, v2); - return map; - } - - public static Map mapOf(K k1, V v1, K k2, V v2, K k3, V v3) { - Map map = mapOf(k1, v1, k2, v2); - map.put(k3, v3); - return map; - } - - public static Map mapOf( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4 - ) { - Map map = mapOf(k1, v1, k2, v2, k3, v3); - map.put(k4, v4); - return map; - } - - public static Map mapOf( - K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5 - ) { - Map map = mapOf(k1, v1, k2, v2, k3, v3, k4, v4); - map.put(k5, v5); - return map; - } - - public static Map.Entry mapEntry(K k, V v) { - return new MapEntry<>(k, v); - } - - private static final class MapEntry implements Map.Entry { - private final K key; - private V value; - - public MapEntry(K key, V value) { - this.key = key; - this.value = value; - } - - @Override - public K getKey() { - return key; - } - - @Override - public V getValue() { - return value; - } - - @Override - public V setValue(V value) { - V old = this.value; - this.value = value; - return old; - } - } -} diff --git a/README.md b/README.md index 46aa0d5..a3f3724 100644 --- a/README.md +++ b/README.md @@ -1,509 +1,585 @@ -# ConfigLib v2 +# ConfigLib -**A Bukkit and BungeeCord library for storing and loading configurations** +**A Minecraft library for mapping objects to YAML configuration files.** -This library facilitates creating, saving and loading configurations by reflectively converting configuration -instances to serializable `Map`s which can be transformed to different representations (e.g. YAML) before being -stored to files or other storage systems. +This library facilitates creating, saving, loading, updating, and documenting YAML configuration +files. It does so by automatically mapping instances of configuration classes to serializable maps +which are then transformed into YAML and saved to some specified file. -Currently this library only supports storing configurations as YAML. However, users may provide their own -storage systems. +## Features -For a step-by-step tutorial see: [Tutorial](https://github.com/Exlll/ConfigLib/wiki/Tutorial) +* Automatic creation, saving, loading, and updating of configuration files +* Support for all primitive types, their wrapper types, and Strings +* Support for `BigInteger`, `BigDecimal`, `LocalDate`, `LocalTime`, and `LocalDateTime` +* Support for (nested) lists, sets, arrays, and maps +* Support for enums and POJOs (+ inheritance!) +* Option to exclude fields from being converted +* Option to add explanatory comments by annotating fields +* Option to format field names before conversion +* Option to customize null handling +* Option to provide custom serializers +* ...and a few more! -## Features -* automatic creation, saving, loading and updating of configurations - * (_YAML_) automatic creation of files and directories -* support for all primitive types, their wrapper types and `String`s -* support for (nested) `List`s, `Set`s and `Map`s -* support for `Enum`s and POJOs -* option to add explanatory comments by annotating classes and their fields -* option to provide custom configuration sources -* option to exclude fields from being converted -* option to provide custom conversion mechanisms -* option to format field names before conversion -* option to execute action before/after loading/saving the configuration -* (_YAML_) option to change the style of the configuration file -* (_YAML_) option to prepend/append text (e.g. color codes) +## Usage example -## General information -#### Supported types -By default, the following types are converted automatically: -- simple types, i.e. primitive types, their wrapper types and `String`s -- `Enum`s -- any type that is annotated as a `ConfigurationElement` -- (nested) `List`s, `Set`s and `Map`s of all the above (e.g. `List`, `Map>`) - - only simple types can be `Map` keys - -For fields whose types are not any of the above, you have two other options: -* Add a custom `Converter` that converts the field's value to any of the above types and back from it. -* If the underlying storage system can handle the type, exclude the field from being converted. - -#### Null values -This library does _not_ support `null` values. All non-primitive fields (e.g. `Integer`, `String`, `List`, `Enum`s) -must be assigned non-null default values. - -#### Adding or removing configuration options -This library supports adding or removing configuration options by simply adding new fields to or removing old fields -from the configuration class. The next time the `save` or `loadAndSave` method is called, the changes will be saved. - -#### Changing the type of configuration options -Changing the type of configuration options is **_not_** supported. **Don't do that.** This may lead to -`ClassCastException`s when loading or accessing the field. This is especially important for generic fields. -For example, you should never change a `List` to a `List`. - -If you need the type of an option to change, add a new field with a different name and the desired type and then -remove the old one. - -#### Subclassing configurations -Currently, subclassing configurations is not supported. If you have an instance of class `B` where `B` is a -subclass of `A` and `A` is a subclass of `YamlConfiguration` and you save or load that instance, then only the -fields of class `B` will be saved or loaded, respectively. - -## How-to (_YAML_) - -For a step-by-step tutorial see: [Tutorial](https://github.com/Exlll/ConfigLib/wiki/Tutorial) - -#### Creating configurations -To create a YAML configuration, create a new class and extend `YamlConfiguration`. If you write a Bukkit plugin, -you can alternatively extend `BukkitYamlConfiguration` which is a subclass of `YamlConfiguration` and can -properly convert Bukkit classes like `Inventory` and `ItemStack` to YAML. - -#### Instantiating configurations -* To instantiate a `YamlConfiguration`, you need to pass a `Path` and optionally a `YamlConfiguration.YamlProperties` -object to its constructor. -* To instantiate a `BukkitYamlConfiguration`, you need to pass a `Path` and optionally a -`BukkitYamlConfiguration.BukkitYamlProperties` object to its constructor. - -If you don't pass a `(Bukkit-)YamlProperties` object, the `(Bukkit-)YamlProperties.DEFAULT` instance will be used. - -#### Instantiating (Bukkit-)YamlProperties -To instantiate a new `(Bukkit-)YamlProperties` object, call `(Bukkit-)YamlProperties.builder()`, -configure the builder and then call its `build()` method. - -Note: The `BukkitYamlProperties` is a subclass of `YamlProperties` but doesn't add any new methods to it. -Its sole purpose is to provide more appropriate defaults to the underlying YAML parser. - -#### Saving and loading configurations -Instances of your configuration class have a `load`, `save` and `loadAndSave` method: -- `load` first tries to load the configuration file and then updates the values of all fields of the configuration -instance with the values it read from the file. - * If the file contains an entry that doesn't have a corresponding field, the entry is ignored. - * If the instance contains a field for which no entry was found, the default value you assigned to that field is kept. -- `save` first converts the configuration instance with its current values to YAML and then tries to dump that YAML -to a configuration file. - * The configuration file is completely overwritten. This means any entries it contains are lost afterwards. -- `loadAndSave` is a convenience method that first calls `load` and then `save`. - * If the file doesn't exist, the configuration instance keeps its default values. Otherwise, the values are - updated with the values read from the file. - * Subsequently the instance is saved so that the values of any newly added fields are also added - to configuration file. - -#### Adding and removing fields -Adding and removing fields is supported. However, changing the type of field is not. - -For example, you can change the following `YamlConfiguration` -```java -class MyConfiguration extends YamlConfiguration { - private String s = "1"; - private double d = 4.2; - // ... -} -``` -to this: -```java -class MyConfiguration extends YamlConfiguration { - private String s = "2"; - private int i = 1; - // ... -} -``` -But you are not allowed to change the type of the variable `d` to `int` (or any other type). +This section contains a short usage example to get you started. The whole range of features is +discussed in the following sections. Information on how to import this library is located at the end +of this documentation. -#### Simple, enum and custom types -The following types are simple types (remember that `null` values are not allowed): - ```java -class MyConfiguration extends YamlConfiguration { - private boolean primBool; - private Boolean refBool = false; - private byte primByte; - private Byte refByte = 0; - private char primChar; - private Character refChar = '\0'; - private short primShort; - private Short refShort = 0; - private int primInt; - private Integer refInt = 0; - private long primLong; - private Long refLong = 0L; - private float primFloat; - private Float refFloat = 0F; - private double primDouble; - private Double refDouble = 0.0; - private String string = ""; - // ... -} -``` +public final class Example { + // To create a configuration annotate the class with @Configuration and make sure that + // it has a no-args constructor. That's it! Now you can add fields to it which can all + // be private; setters are not required! + @Configuration + public static class BaseConfiguration { + private String host = "127.0.0.1"; + private int port = 1234; + // The library supports lists, sets, and maps. + private Set blockedAddresses = Set.of(); + // Fields can be ignored by making them final, transient, static or by + // annotating them with @Ignore. + private final double ignoreMe = 3.14; + } -Enums are supported: + // This class does not need to be annotated with @Configuration because it + // extends a class which already is! + public static final class UserConfiguration extends BaseConfiguration { + // You can add comments with the @Comment annotation. Each string in the comment + // array is written (as a comment) on a new line. + @Comment({"The admin user has full access.", "Choose a proper password!"}) + User admin = new User("root", "toor"); // The User class is a @Configuration! + List blockedUsers = List.of( + new User("user1", null), // null values are supported + new User("user2", null) + ); + } -```java -class MyConfiguration extends YamlConfiguration { - private Material material = Material.AIR; - //... -} -``` + @Configuration + public static final class User { + private String username; + @Comment("Please choose a strong password.") + private String password; -Custom classes are supported if they are annotated as `ConfigurationElement`s and if they have a no-args constructor. -Custom classes can have fields whose values are also instances of custom classes. + // Configuration classes require a no-args constructor. + // The constructor can be private though. + private User() {} -```java -@ConfigurationElement -class MyCustomClass1 {/* fields etc.*/} + public User(String username, String password) {/* initialize */} + } -@ConfigurationElement -class MyCustomClass2 { - private MyCustomClass1 cls1 = new MyCustomClass1(); - // ... -} + public static void main(String[] args) { + final var props = YamlConfigurationProperties.newBuilder().build(); + final var store = new YamlConfigurationStore<>(UserConfiguration.class, props); + final var configFile = Paths.get("/tmp/config.yml"); + + // Save a new instance to the configuration file + store.save(new UserConfiguration(), configFile); -class MyConfiguration extends YamlConfiguration { - private MyCustomClass2 cls2 = new MyCustomClass2(); - // ... + // Load a new instance from the configuration file + UserConfiguration configuration = store.load(configFile); + System.out.println(configuration.admin.username); + System.out.println(configuration.blockedUsers); + + // Modify and save the configuration file + configuration.blockedUsers.add(new User("user3", "pass3")); + store.save(configuration, configFile); + } } ``` -#### `List`s, `Set`s, `Map`s -Lists, sets and maps of simple types can be used as is and don't need any special treatment. -```java -class MyConfiguration extends YamlConfiguration { - private Set ints = new HashSet<>(); - private List strings = new ArrayList<>(); - private Map doubleByBool = new HashMap<>(); - // ... -} +By running the above code, a new YAML configuration is created at `/tmp/config.yml`. Its content +looks like this: + +```yaml +host: 127.0.0.1 +port: 1234 +blockedAddresses: [ ] +# The admin user has full access. +# Choose a proper password! +admin: + username: root + # Please choose a strong password. + password: toor +blockedUsers: + - username: user1 + - username: user2 + - username: user3 + password: pass3 ``` -Note: Even though sets are supported, their YAML-representation is 'pretty ugly', so it's better to use lists instead. -If you need set behavior, you can internally use lists and convert them to sets using the `preSave/postLoad`-hooks. +Two things are noticeable here: + +1. Not every user in the `blockedUsers` list has a `password` mapping. This is because null values + are not output by default. That behavior can be changed by the builder. +2. The password of the user with username `user3` that has no comment. This is due to limitations of + the YAML library. Configurations in lists, sets, or maps cannot have their comments printed. + +## General information + +In the following sections the term _configuration type_ refers to any non-generic class that is +directly or indirectly (i.e. through subclassing) annotated with +`@de.exlll.configlib.Configuration`. Accordingly, the term _configuration_ refers to an instance of +such a type. + +### Declaring configuration types + +To declare a configuration type, annotate a class with `@Configuration` and make sure that it has a +no-args constructor. The no-args constructor can be set `private`. Inner classes (i.e. the ones that +are nested but not `static`) have an implicit synthetic constructor with at least one argument and +are therefore not supported. + +Add fields to your class whose type is any of the supported types listed in the next section. You +should initialize all fields of reference types with non-null default values, though you can leave +them null. Handling of null values is discussed in one of the sections further below. -Lists, sets and maps that contain other types (e.g. custom types or enums) must use the `@ElementType` annotation. -Only simple types can be used as map keys. +### Supported types + +A configuration type may only contain fields of the following types: + +| Type class | Types | +|------------------------|--------------------------------------------------------------------| +| Boolean types | `boolean`, and `Boolean` | +| Integer types | `byte`, `short`, `int`, `long`, and their respective wrapper types | +| Floating point types | `float`, `double`, and their respective wrapper types | +| Characters and strings | `char`, `Character`, `String` | +| Big numeric types | `BigInteger`, `BigDecimal` | +| Time related types | `LocalTime`, `LocalDate`, `LocalDateTime` | +| Enums | Any Java enum | +| Configurations | Any configuration type | +| Collections | (Nested) Lists, sets, maps*, or arrays of previously listed types | + +(*) Map keys cannot be in the `Configurations` or `Collections` type class. + + +

+ Examples of supported types + +The following class contains examples of types that this library supports: ```java -@ConfigurationElement -class MyCustomClass {/* fields etc.*/} - -class MyConfiguration extends YamlConfiguration { - @ElementType(Material.class) - private List materials = new ArrayList<>(); - - @ElementType(MyCustomClass.class) - private Set customClasses = new HashSet<>(); - - @ElementType(MyCustomClass.class) - private Map customClassesMap = new HashMap<>(); - // ... +public final class SupportedTypes { + boolean supported; + Character supported; + ExampleEnum supported; // where 'ExampleEnum' is some Java enum type + ExampleConf supported; // where 'ExampleConf' is another configuration type + java.awt.Point supported; // only if a custom serializer is registered + /* collection types */ + List supported; + Set supported; + LocalDate[] supported; + Map supported; + /* nested collection types */ + List> supported; + int[][] supported; + Map>>> supported; } ``` -Lists, sets and maps can be nested. If nested collections contain custom types, you must specify the -nesting level using the `@ElementType` annotation. Examples: +
-* `List` requires a nesting level of 0, which is the default value, so you don't have to set it -* `List>` requires a nesting level of 1 -* `List>>` requires a nesting level of 2 -* `List>` requires a nesting level of 1 -* `List>>` requires a nesting level of 2 +
+ Examples of unsupported types + +The following class contains examples of types that this library does (and will) not support: ```java -@ConfigurationElement -class MyCustomClass {/* fields etc.*/} - -class MyConfiguration extends YamlConfiguration { - private List> listsList = new ArrayList<>(); - private Set> setsSet = new HashSet<>(); - private Map> mapsMap = new HashMap<>(); - - @ElementType(value = MyCustomClass.class, nestingLevel = 1) - private List> customClassListsList = new ArrayList<>(); - - @ElementType(value = MyCustomClass.class, nestingLevel = 1) - private Set> customClassSetsSet = new HashSet<>(); - - @ElementType(value = MyCustomClass.class, nestingLevel = 1) - private Map> customClassMapsMap = new HashMap<>(); - // ... +public final class UnsupportedTypes { + Map unsupported; // invalid map key + Map, String> unsupported; // invalid map key + Box unsupported; // custom parameterized type + List unsupported; // wildcard type + List unsupported; // wildcard type + List[] unsupported; // wildcard type + T unsupported; // type variable + List unsupported; // raw type + List[] unsupported; // raw type + List[] unsupported; // generic array type + Set[] unsupported; // generic array type + Map[] unsupported; // generic array type } ``` -#### Adding comments -You can add comments to a configuration class or a its field by using the `@Comment` annotation. -Class comments are saved at the beginning of a configuration file. -```java -@Comment({"A", "", "B"}) -class MyConfiguration extends YamlConfiguration { - @Comment("the x") - private int x; - @Comment({"", "the y"}) - private int y; - // ... +
+ +### Loading and saving configurations + +There are two ways to load and save configurations. Which way you choose depends on your liking. +Both ways have three methods in common: + +* `save` saves a configuration to a file +* `load` creates a new configuration instance and populates it with values taken from a file +* `update` is a combination of `load` and `save` and the method you'd usually want to use: it takes + care of creating the configuration file if it does not exist and updates it otherwise to reflect + changes to (the fields of) the configuration type. + +
+ Example of update behavior when configuration file exists + +Let's say you have the following configuration type: + +```java +@Configuration +public final class C { + int i = 10; + int j = 11; } ``` -Empty strings are represented as newlines (i.e. lines that don't start with '# '). -#### Executing pre-save and post-load actions -To execute pre-save and post-load actions, override `preSave()` and `postLoad()`, respectively. -```java -class MyConfiguration extends YamlConfiguration { - @Override - protected void preSave(){ /* do something ... */} - - @Override - protected void postLoad(){ /* do something ... */} - // ... -} +... and a YAML configuration file that contains: + +```yaml +i: 20 +k: 30 ``` -#### Excluding fields from being converted -To exclude fields from being converted, annotate them with the `@NoConvert` annotation. This may be useful if the -configuration knows how to (de-)serialize instances of that type. For example, a `BukkitYamlConfiguration` knows how -to serialize `ItemStack` instances. +Now, when you use one of the methods below to call `update` for that configuration type and file, +the configuration instance that `update` returns will have its `i` variable initialized to `20` +and its `j` variable will have its default of `11`. After the operation, the configuration file will +contain: -```java -class MyConfiguration extends BukkitYamlConfiguration { - @NoConvert - private ItemStack itemStack = new ItemStack(Material.STONE, 1); - // ... -} +```yaml +i: 20 +j: 11 ``` -#### Changing configuration properties -To change the properties of a configuration, use the properties builder object. +
-##### Formatting field names -To format field names before conversion, configure the properties builder to use a custom `FieldNameFormatter`. -You can either define your own `FieldNameFormatter` or use one from the `FieldNameFormatters` enum. +To exemplify the usage of these three methods we assume for the following sections that you have +implemented the configuration type below and have access to some regular `java.nio.file.Path` +object `configurationFile`. -```java -YamlProperties properties = YamlProperties.builder() - .setFormatter(FieldNameFormatters.LOWER_UNDERSCORE) - // ... - .build(); +```java +@Configuration +public final class Config { /* some fields */ } ``` -Alternatively, you can annotate your `Configuration` class with the `@Format` annotation. The `FieldNameFormatter` -returned by this annotation takes precedence over the `FieldNameFormatter` returned by a `Properties` object. +#### Way 1 -```java -@Format(FieldNameFormatters.UPPER_UNDERSCORE) -class MyConfiguration extends YamlConfiguration { - // ... -} +The first way is to create a configuration store and use it directly to save, load, or update +configurations. + +```java +YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder().build(); +YamlConfigurationStore store = new YamlConfigurationStore<>(Config.class, properties); + +Config config1 = store.load(configurationFile); +store.save(config1, configurationFile); +Config config2 = store.update(configurationFile); ``` -Note: You should neither remove nor replace a formatter with one that has a different formatting style because this -could break existing configurations. +#### Way 2 -##### (_YAML_) Prepending/appending text -To prepend or append comments to a configuration file, use the `setPrependedComments` and `setAppendedComments` methods, -respectively. +The second way is to use the static methods from the `Configurations` class. -```java -YamlProperties properties = YamlProperties.builder() - .setPrependedComments(Arrays.asList("A", "B")) - .setAppendedComments(Arrays.asList("C", "D")) - // ... - .build(); +```java +Config config1 = Configurations.loadYamlConfiguration(configurationFile, Config.class); +Configurations.saveYamlConfiguration(configurationFile, Config.class, config1); +Config config2 = Configurations.updateYamlConfiguration(configurationFile, Config.class); ``` -##### (_YAML_) Changing the style of the configuration file -To change the configuration style, use the `setConstructor`, `setRepresenter`, `setOptions` and `setResolver` methods. -These methods change the behavior of the underlying YAML-parser. -See [snakeyaml-Documentation](https://bitbucket.org/asomov/snakeyaml/wiki/Documentation). +Each of these methods has two additional overloads: One that takes a properties object and another +that lets you configure a properties object builder. For example, the overloads for the +`loadYamlConfiguration` method are: + +```java +// overload 1 +YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder() + .inputNulls(true) + .outputNulls(false) + .build(); +Config c1 = Configurations.loadYamlConfiguration(configurationFile, Config.class, properties); + +// overload 2 +Config c2 = Configurations.loadYamlConfiguration( + configurationFile, + Config.class, + builder -> builder.inputNulls(true).outputNulls(false) +); +``` -```java -YamlProperties properties = YamlProperties.builder() - .setConstructor(...) - .setRepresenter(/* */) - .setOptions(/* */) - .setResolver(/* */) - // ... - .build(); +#### Configuration properties + +Instances of the `ConfigurationProperties` class allow customization of how configurations are +stored and loaded. To create such an instance, instantiate a new builder using +the `YamlConfigurationProperties.newBuilder()` method, configure it, and finally call its `build()` +method. Check out the several methods of the builder to see which configuration options are +available. + +#### Ignoring and filtering fields + +Fields that are `final`, `static`, `transient` or annotated with `@Ignore` are neither serialized +nor updated during deserialization. You can filter out additional fields by providing an instance of +`FieldFilter` to the configuration properties. + +#### Handling of `null` values + +Configuration properties let you configure how `null` values are handled when serializing and +deserializing a configuration: + +* By setting `outputNulls` to false, fields and collection elements that are null are not output. + Any comments that belong to such fields are also not written. +* By setting `inputNulls` to false, fields and collection elements that are null are not input. That + means that fields will keep their default values. + +
+ Example null handling configuration + +The following code forbids null values to be output but allows null values to be input. By default, +both are forbidden which makes the call to `outputNulls` in this case redundant. + +```java +YamlConfigurationProperties.newBuilder() + .outputNulls(false) + .inputNulls(true) + .build(); ``` -Note: Changing the configuration style may break adding comments using the `@Comment` annotation. +
-##### Adding field filters -If your configuration has a lot of fields and you want to exclude some of these fields without -making them final, static or transient, you can configure your properties object to use additional -`FieldFilter`s. A `FieldFilter` filters the fields of a configuration class by a specified criterion. +#### Field formatting -For example, if you only want to include fields whose names don't start with _ignore_, you would add -the following filter: +You can define how fields are formatted by configuring the configuration properties with a custom +formatter. Field formatters are implementations of the `FieldFormatter` interface. You can implement +this interface yourself or use one of the several formatters this library provides. These +pre-defined formatters can be found in the `FieldFormatters` class. -```java -YamlProperties properties = YamlProperties.builder() - .addFilter(field -> !field.getName().startsWith("ignore")) - // ... - .build(); +
+ Example FieldFormatter configuration + +The following code formats fields using the `IDENTITY` formatter (which is the default). + +```java +YamlConfigurationProperties.newBuilder() + .setFieldFormatter(FieldFormatters.IDENTITY) + .build(); ``` -Note: A filter is not evaluated for a field if the field has already been filtered or by some -other `FieldFilter`. +
-#### Adding custom converters -Any field can be converted using a custom converter. This can be useful if you don't like the default -conversion mechanism or if you have classes that cannot be annotated as `ConfigurationElement`s -(e.g. because they are not under your control). +### Subclassing -To create a new converter, you have to implement the `Converter` interface where `F` represents -the type of the field and `T` represents the type of the value to which the field value is converted. +Subclassing of configurations types is supported. Subclasses of configuration types don't need to be +annotated with `@Configuration`. When a configuration is written, the fields of parent classes +are written before the fields of the child in a top to bottom manner. Parent configurations can +be `abstract`. -```java -import java.awt.Point; +#### Shadowing of fields -class PointStringConverter implements Converter { - @Override - public String convertTo(Point element, ConversionInfo info) { - return element.x + ":" + element.y; - } +Shadowing of fields refers to the situation where a subclass of configuration has a field that has +the same name as a field in one of its super classes. Shadowing of fields is currently not +supported. (This restriction might easily be lifted. If you need this feature, please open an issue +and describe how to handle name clashes.) - @Override - public Point convertFrom(String element, ConversionInfo info) { - String[] coordinates = element.split(":"); - int x = Integer.parseInt(coordinates[0]); - int y = Integer.parseInt(coordinates[1]); - return new Point(x, y); - } +### Comments + +The fields of a configuration can be annotated with the `@Comment` annotation. This annotation takes +an array of strings. Each of these strings is written onto a new line as a comment. Empty strings +are written as newlines. + +
+ Example of @Comment usage + +Serializing the following configuration as YAML ... + +```java +@Configuration +public final class ExampleConfiguration { + @Comment({"Hello", "", " ", "World"}) + private String commentedField = "commented field"; } ``` -To use your custom converter, pass its class to the `@Convert` annotation. -```java -class MyConfiguration extends YamlConfiguration { - @Convert(PointStringConverter.class) - private Point point = new Point(2, 3); - //... -} +... results in the YAML file shown below: + +```yaml + # Hello + + # + # World + commentedField: commented field ``` -Note: Only a single converter instance is created which is cached. +
-## Example +If a configuration type _C_ that defines comments is used (as a field) within another configuration +type, the comments of _C_ are written with the proper indentation. However, if instances of _C_ are +stored inside a collection, their comments are not printed when the collection is written. -For a step-by-step tutorial of a more complex example see: -[Tutorial](https://github.com/Exlll/ConfigLib/wiki/Tutorial) +### Recursive type definitions + +Recursive type definitions are currently not allowed but might be supported in a future version if +this feature is requested. + +
+ Examples of recursive type definitions + +Neither direct nor indirect recursive type definitions are supported. ```java -import de.exlll.configlib.annotation.Comment; -import de.exlll.configlib.annotation.ConfigurationElement; -import de.exlll.configlib.configs.yaml.BukkitYamlConfiguration; -import de.exlll.configlib.configs.yaml.BukkitYamlConfiguration.BukkitYamlProperties; -import de.exlll.configlib.format.FieldNameFormatters; -import org.bukkit.plugin.java.JavaPlugin; - -import java.io.File; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; - -@ConfigurationElement -class Credentials { - private String username; - private String password; - - // ConfigurationElements must have a no-args constructor - Credentials() { - this("", ""); // default values must be non-null +public final class RecursiveTypDefinitions { + // Direct recursive definition + @Configuration + static final class R { + R r; } - Credentials(String username, String password) { - this.username = username; - this.password = password; + // Indirect recursive definition + @Configuration + static final class R1 { + R2 r2; } - String getUsername() { return username; } + @Configuration + static final class R2 { + R1 r1; + } } +``` + +
+ +### Type conversion and custom serializers + +Before instances of the types listed in the [supported types](#supported-types) section can be +stored, they need to be converted into serializable types (i.e. into types the underlying YAML +library knows how to handle). The conversion happens according to the following table: + +| Source type | Target type | +|------------------------|------------------| +| Boolean types | `Boolean` | +| Integer types | `Long` | +| Floating point types | `Double` | +| Characters and strings | `String` | +| Big numeric types | `String` | +| Time related types | `String` | +| Enums | `String` | +| Configurations | `Map` | +| `Set` | `List`* | +| `List` | `List` | +| `S[]` | `List` | +| `Map` | `Map` | + +(*) By default, sets are serialized as lists. This can be changed through the configuration +properties. This also means that `Set`s are valid target types. + +#### Serializer selection + +To convert the value of a field `F` with (source) type `S` into a serializable value of some +target type, a serializer has to be selected. Serializers are instances of +the `de.exlll.configlib.Serializer` interface and are selected based on `S`. Put differently, +serializers are always selected based on the compile-time type of `F` and never on the runtime type +of its value. + +This distinction makes a difference (and might lead to confusion) when you have fields whose type is +a configuration type or a collection of some configuration type, and you extend that configuration +type. Concretely, assume you have and written two configuration types `A` and `B` +where `B extends A`. Then, if you use `A a = new B()` in your main configuration, only the fields of +a `A` will be stored when you save your main configuration. That is because the serializer of +field `a` was selected based on the compile-time type of `a` which is `A` and not `B`. The same +happens if you have a `List` and put instances of `B` (or some other subclass of `A`) in it. + +#### Custom serializers + +If you want to add support for a type whose class is not annotated with `@Configuration`, you can +register a custom serializer. Serializers are instances of the `de.exlll.configlib.Serializer` +interface. When implementing that interface you have to make sure that you convert your source type +into one of the valid target types listed in the table above. The serializer then has to be +registered through a `ConfigurationProperties` object. + +
+ Example Serializer implementation + +The following `Serializer` serializes instances of `java.awt.Point` into strings. -@Comment("MAIN-DB CONFIG") -class DatabaseConfig extends BukkitYamlConfiguration { - private String host = "localhost"; - @Comment("must be greater than 1024") - private int port = 3306; - private Credentials adminAccount = new Credentials("admin", "123"); - private List blockedUsers = Arrays.asList("root", "john"); - - /* You can use the other constructor instead which uses the - * BukkitYamlProperties.DEFAULT instance. */ - DatabaseConfig(Path path, BukkitYamlProperties properties) { - super(path, properties); +```java +public final class PointSerializer implements Serializer { + @Override + public String serialize(Point element) { + return element.x + ":" + element.y; } - Credentials getAdminAccount() { return adminAccount; } + @Override + public Point deserialize(String element) { + String[] parts = element.split(":"); + int x = Integer.parseInt(parts[0]); + int y = Integer.parseInt(parts[1]); + return new Point(x, y); + } } +``` -public final class DatabasePlugin extends JavaPlugin { - @Override - public void onEnable() { - /* Creating a properties object is not necessary if the other - * DatabaseConfig constructor is used. */ - BukkitYamlProperties props = BukkitYamlProperties.builder() - .setPrependedComments(Arrays.asList("Author: Pete", "Version: 1.0")) - .setFormatter(FieldNameFormatters.LOWER_UNDERSCORE) - .build(); +
- Path configPath = new File(getDataFolder(), "config.yml").toPath(); +Custom serializers takes precedence over the serializers provided by this library. - DatabaseConfig config = new DatabaseConfig(configPath, props); - config.loadAndSave(); +### Changing the type of fields + +Changing the type of fields is not supported. If you change the type of one of your fields but your +configuration file still contains a value of the old type, a type mismatch will occur when reading +that file. Instead, remove the old field and add a new one with a different name. - System.out.println(config.getAdminAccount().getUsername()); - } -} -``` ## Import + +**INFO:** I'm currently looking for an easier way for you to import this library that does not +require authentication with GitHub. Please check +this [issue](https://github.com/Exlll/ConfigLib/issues/12) if you have authentication problems. + +To use this library, import it into your project with either Maven or Gradle as shown in the two +sections below. This library has additional dependencies (namely, a YAML parser) which are not +included in the artifact you import. + +This repository provides plugin versions of this library which bundle all its dependencies, so you +don't have to worry about them. Also, these versions make it easier for you to update this library +if you have written multiple plugins that use it. + +The plugin versions can be downloaded from +the [releases page](https://github.com/Exlll/ConfigLib/releases) where you can identify them by +their `-paper-`, `-waterfall-`, and `-velocity-` infix and `-all` suffix. Other than that, the +plugin versions currently don't add any additional functionality. If you use these versions, don't +forget to add them as a dependency in the `plugin.yml` (for Paper and Waterfall) or to the +dependencies array (for Velocity) of your own plugin. + +Alternatively, if you don't want to use an extra plugin, you can shade the `-core` version and the +YAML parser yourself. + #### Maven -```xml + +```xml de.exlll https://maven.pkg.github.com/Exlll/ConfigLib - - - de.exlll - configlib-bukkit - 2.2.0 - - - de.exlll - configlib-bungee - 2.2.0 + configlib-core + 3.0.0 ``` + #### Gradle + ```groovy repositories { maven { url 'https://maven.pkg.github.com/Exlll/ConfigLib' } } -dependencies { - // for Bukkit plugins - implementation group: 'de.exlll', name: 'configlib-bukkit', version: '2.2.0' - // for Bungee plugins - implementation group: 'de.exlll', name: 'configlib-bungee', version: '2.2.0' -} +dependencies { implementation 'de.exlll:configlib-core:3.0.0' } ``` ```kotlin repositories { maven { url = uri("https://maven.pkg.github.com/Exlll/ConfigLib") } } -dependencies { - // for Bukkit plugins - implementation("de.exlll:configlib-bukkit:2.2.0") - // for Bungee plugins - implementation("de.exlll:configlib-bungee:2.2.0") -} -``` \ No newline at end of file +dependencies { implementation("de.exlll:configlib-core:3.0.0") } +``` + +## Future work + +This section contains ideas for upcoming features. If you want any of these to happen any time soon, +please [open an issue](https://github.com/Exlll/ConfigLib/issues/new) where we can discuss the +details. + +- Optional fields +- Post load / pre save hooks +- TOML support +- Change the order of fields in parent/child class scenarios +- Recursive definitions \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index a6f15d3..4e2d584 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,77 +1,4 @@ -plugins { - java - idea - `maven-publish` -} - allprojects { group = "de.exlll" - version = "2.2.0" -} - -subprojects { - apply(plugin = "java") - apply(plugin = "idea") - apply(plugin = "maven-publish") - - java.sourceCompatibility = JavaVersion.VERSION_1_8 - java.targetCompatibility = JavaVersion.VERSION_1_8 - - repositories { - mavenCentral() - } - - dependencies { - testImplementation("org.junit.jupiter:junit-jupiter-api:5.0.3") - testImplementation("org.junit.platform:junit-platform-runner:1.0.3") - testImplementation("org.junit.platform:junit-platform-suite-api:1.0.3") - testImplementation("org.hamcrest:hamcrest-all:1.3") - testImplementation("com.google.jimfs:jimfs:1.1") - } - - publishing { - repositories { - maven { - name = "GitHubPackages" - url = uri("https://maven.pkg.github.com/Exlll/ConfigLib") - credentials { - username = System.getenv("GITHUB_ACTOR") - password = System.getenv("GITHUB_TOKEN") - } - } - publications { - register("gpr") { - from(components["java"]) - } - } - } - } -} - -project(":configlib-core") { - dependencies { - implementation("org.yaml:snakeyaml:1.20") - } -} -project(":configlib-bukkit") { - repositories { - maven(url = "https://hub.spigotmc.org/nexus/content/repositories/snapshots/") - } - dependencies { - implementation(project(":configlib-core")) - implementation("org.bukkit:bukkit:1.12.2-R0.1-SNAPSHOT") - } - - tasks.jar { from(project(":configlib-core").sourceSets["main"].output) } -} -project(":configlib-bungee") { - repositories { - maven(url = "https://oss.sonatype.org/content/repositories/snapshots") - } - dependencies { - implementation(project(":configlib-core")) - implementation("net.md-5:bungeecord-api:1.12-SNAPSHOT") - } - - tasks.jar { from(project(":configlib-core").sourceSets["main"].output) } + version = "3.0.0" } \ No newline at end of file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 0000000..c66850b --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() + gradlePluginPortal() +} + +dependencies { + implementation("gradle.plugin.com.github.johnrengelman:shadow:7.1.2") +} \ No newline at end of file diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 0000000..e69de29 diff --git a/buildSrc/src/main/kotlin/java-config.gradle.kts b/buildSrc/src/main/kotlin/java-config.gradle.kts new file mode 100644 index 0000000..c27fee3 --- /dev/null +++ b/buildSrc/src/main/kotlin/java-config.gradle.kts @@ -0,0 +1,91 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +plugins { + java + idea + `maven-publish` + id("com.github.johnrengelman.shadow") +} + +val shade = configurations.create("shade") + +configurations { + compileClasspath.get().extendsFrom(shade) +} + +val shadowJarTask = tasks.getByName("shadowJar") { + configurations = listOf(shade) + relocate("org.snakeyaml.engine", "de.exlll.configlib.org.snakeyaml.engine") +} + +val coreProjectCheckTask = project(":configlib-core").tasks.getByName("check"); + +shadowJarTask.dependsOn( + tasks.named("check"), + coreProjectCheckTask +) + +tasks.getByName("build").dependsOn(coreProjectCheckTask) + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + + withJavadocJar() + withSourcesJar() +} + +tasks.getByName("test") { + useJUnitPlatform() +} + +repositories { + mavenCentral() + maven(url = "https://papermc.io/repo/repository/maven-public/") +} + +dependencies { + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.8.2") + testImplementation("org.junit.jupiter:junit-jupiter-engine:5.8.2") + testImplementation("org.junit.platform:junit-platform-runner:1.8.2") + testImplementation("org.junit.platform:junit-platform-suite-api:1.8.2") + testImplementation("org.mockito:mockito-inline:4.2.0") + testImplementation("org.mockito:mockito-junit-jupiter:4.2.0") + testImplementation("org.hamcrest:hamcrest-all:1.3") + testImplementation("com.google.jimfs:jimfs:1.2") +} + +val javaComponent = components["java"] as AdhocComponentWithVariants +javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { + skip() +} + +publishing { + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/Exlll/ConfigLib") + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } + + val moduleId = project.name.split("-")[1].toLowerCase() + val publicationName = moduleId.capitalize() + + publications { + register(publicationName) { + from(components["java"]) + } + } +} + +idea { + module { + isDownloadJavadoc = true + isDownloadSources = true + } +} \ No newline at end of file diff --git a/configlib-core/build.gradle.kts b/configlib-core/build.gradle.kts new file mode 100644 index 0000000..9e59321 --- /dev/null +++ b/configlib-core/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + `java-config` +} + +dependencies { + shade("org.snakeyaml:snakeyaml-engine:2.3") + implementation("org.snakeyaml:snakeyaml-engine:2.3") +} \ No newline at end of file diff --git a/configlib-core/src/main/java/de/exlll/configlib/Comment.java b/configlib-core/src/main/java/de/exlll/configlib/Comment.java new file mode 100644 index 0000000..de8214a --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/Comment.java @@ -0,0 +1,21 @@ +package de.exlll.configlib; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the annotated element is saved together with explanatory + * comments describing it. + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Comment { + /** + * Returns the comments of the annotated field. + * + * @return field comments + */ + String[] value(); +} \ No newline at end of file diff --git a/configlib-core/src/main/java/de/exlll/configlib/CommentNode.java b/configlib-core/src/main/java/de/exlll/configlib/CommentNode.java new file mode 100644 index 0000000..2376c9e --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/CommentNode.java @@ -0,0 +1,29 @@ +package de.exlll.configlib; + +import java.util.List; + +/** + * Holds the comments of a field as well as a list of field names. The list of + * field names contains the names of all fields which led to this field from the + * root of the configuration object. For example, for the following situation the + * {@code CommentNode} of the field {@code fn2} would hold {@code comments} and + * {@code fieldNames} lists that contain the values {@code ["Hello World"]} and + * {@code ["fn0", "fn1", "fn2"]}, respectively. + *
+ * class A {
+ *     B fn0 = new B();
+ * }
+ * class B {
+ *     C fn1 = new C();
+ * }
+ * class C {
+ *     {@code @Comment("Hello world")}
+ *     int fn2;
+ * }
+ *  
+ * + * @param comments + * @param fieldNames + */ +record CommentNode(List comments, List fieldNames) {} + diff --git a/configlib-core/src/main/java/de/exlll/configlib/CommentNodeExtractor.java b/configlib-core/src/main/java/de/exlll/configlib/CommentNodeExtractor.java new file mode 100644 index 0000000..1b6a635 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/CommentNodeExtractor.java @@ -0,0 +1,83 @@ +package de.exlll.configlib; + +import java.lang.reflect.Field; +import java.util.*; + +final class CommentNodeExtractor { + private final FieldFilter fieldFilter; + private final FieldFormatter fieldFormatter; + private final boolean outputNull; + + CommentNodeExtractor(ConfigurationProperties properties) { + this.fieldFilter = Validator.requireNonNull(properties.getFieldFilter(), "field filter"); + this.fieldFormatter = Validator.requireNonNull(properties.getFieldFormatter(), "field formatter"); + this.outputNull = properties.outputNulls(); + } + + private record State(Iterator iterator, Object configuration) {} + + /** + * Extracts {@code CommentNode}s of the given configuration in a DFS manner. + * The nodes are returned in the order in which they were found. + * + * @param configuration the configuration from which the nodes are extracted + * @return the nodes in the order in which they are found + * @throws IllegalArgumentException if {@code configuration} is not a configuration + * @throws NullPointerException if {@code configuration} is null + */ + public Queue extractCommentNodes(final Object configuration) { + Validator.requireConfiguration(configuration.getClass()); + final Queue result = new ArrayDeque<>(); + final var fnameStack = new ArrayDeque<>(List.of("")); + final var stateStack = new ArrayDeque<>(List.of( + new State(configurationFields(configuration), configuration) + )); + + State state; + while (!stateStack.isEmpty()) { + state = stateStack.removeLast(); + fnameStack.removeLast(); + + while (state.iterator.hasNext()) { + final var field = state.iterator.next(); + final var value = Reflect.getValue(field, state.configuration); + + if ((value == null) && !outputNull) + continue; + + final var commentNode = createNodeIfFieldHasComment(field, fnameStack); + commentNode.ifPresent(result::add); + + if ((value == null) || !Reflect.isConfiguration(field.getType())) + continue; + + stateStack.addLast(new State(state.iterator, state.configuration)); + fnameStack.addLast(fieldFormatter.format(field)); + state = new State(configurationFields(value), value); + } + } + + return result; + } + + private Optional createNodeIfFieldHasComment( + Field field, + Deque fileNameStack + ) { + if (field.isAnnotationPresent(Comment.class)) { + final var comments = field.getAnnotation(Comment.class).value(); + final var fieldName = fieldFormatter.format(field); + final var fieldNames = new ArrayList<>(fileNameStack.stream().toList()); + fieldNames.add(fieldName); + final var result = new CommentNode(Arrays.asList(comments), fieldNames); + return Optional.of(result); + } + return Optional.empty(); + } + + private Iterator configurationFields(Object configuration) { + return FieldExtractors.CONFIGURATION.extract(configuration.getClass()) + .filter(fieldFilter) + .iterator(); + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/Configuration.java b/configlib-core/src/main/java/de/exlll/configlib/Configuration.java new file mode 100644 index 0000000..08f9e1e --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/Configuration.java @@ -0,0 +1,13 @@ +package de.exlll.configlib; + +import java.lang.annotation.*; + +/** + * Indicates the annotated type is a configuration. + *

+ * Configuration classes must have a no-args constructor. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Inherited +public @interface Configuration {} diff --git a/ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationException.java b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationException.java similarity index 63% rename from ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationException.java rename to configlib-core/src/main/java/de/exlll/configlib/ConfigurationException.java index 2c5bdaf..7d42aee 100644 --- a/ConfigLib-Core/src/main/java/de/exlll/configlib/ConfigurationException.java +++ b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationException.java @@ -1,9 +1,7 @@ package de.exlll.configlib; /** - * Signals that an error occurred during the (de-)serialization of a configuration. - *

- * The cause of this exception is most likely some misconfiguration. + * Signals that an error occurred during the serialization or deserialization of a configuration. */ public final class ConfigurationException extends RuntimeException { ConfigurationException(String message) { diff --git a/configlib-core/src/main/java/de/exlll/configlib/ConfigurationProperties.java b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationProperties.java new file mode 100644 index 0000000..3303ac5 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationProperties.java @@ -0,0 +1,223 @@ +package de.exlll.configlib; + +import java.util.HashMap; +import java.util.Map; + +import static de.exlll.configlib.Validator.requireNonNull; + +/** + * A collection of values used to configure the serialization of configurations. + */ +class ConfigurationProperties { + private final Map, Serializer> serializersByType; + private final FieldFormatter formatter; + private final FieldFilter filter; + private final boolean outputNulls; + private final boolean inputNulls; + private final boolean serializeSetsAsLists; + + /** + * Constructs a new instance of this class with values taken from the given builder. + * + * @param builder the builder used to initialize the fields of this class + * @throws NullPointerException if the builder or any of its values is null + */ + protected ConfigurationProperties(Builder builder) { + this.serializersByType = Map.copyOf(builder.serializersByType); + this.formatter = requireNonNull(builder.formatter, "field formatter"); + this.filter = requireNonNull(builder.filter, "field filter"); + this.outputNulls = builder.outputNulls; + this.inputNulls = builder.inputNulls; + this.serializeSetsAsLists = builder.serializeSetsAsLists; + } + + /** + * Constructs a new {@code Builder} with default values. + * + * @return newly constructed {@code Builder} + */ + public static Builder newBuilder() { + return new BuilderImpl(); + } + + private static final class BuilderImpl extends Builder { + @Override + protected BuilderImpl getThis() {return this;} + + @Override + public ConfigurationProperties build() {return new ConfigurationProperties(this);} + } + + /** + * A builder class for constructing {@code ConfigurationProperties}. + * + * @param the type of builder + */ + public static abstract class Builder> { + private final Map, Serializer> serializersByType = new HashMap<>(); + /* change setter JavaDoc if default values are changed */ + private FieldFormatter formatter = FieldFormatters.IDENTITY; + private FieldFilter filter = FieldFilters.DEFAULT; + private boolean outputNulls = false; + private boolean inputNulls = false; + private boolean serializeSetsAsLists = true; + + protected Builder() {} + + /** + * Sets the field filter. The given filter is applied in addition to and + * after the default filter. + * + * @param filter the filter + * @return this builder + * @throws NullPointerException if {@code filter} is null + */ + public final B setFieldFilter(FieldFilter filter) { + this.filter = requireNonNull(filter, "field filter"); + return getThis(); + } + + /** + * Sets the field formatter. + *

+ * The default value is a formatter that returns the name of the field. + * + * @param formatter the formatter + * @return this builder + * @throws NullPointerException if {@code formatter} is null + */ + public final B setFieldFormatter(FieldFormatter formatter) { + this.formatter = requireNonNull(formatter, "field formatter"); + return getThis(); + } + + /** + * Adds a serializer for the given type. If this library already provides a serializer + * for the given type (e.g. {@code BigInteger}, {@code LocalDate}, etc.) the serializer + * added by this method takes precedence. + * + * @param serializedType the class of the type that is serialized + * @param serializer the serializer + * @param the type that is serialized + * @return this builder + * @throws NullPointerException if any argument is null + */ + public final B addSerializer(Class serializedType, Serializer serializer) { + requireNonNull(serializedType, "serialized type"); + requireNonNull(serializer, "serializer"); + serializersByType.put(serializedType, serializer); + return getThis(); + } + + /** + * Sets whether fields or collection elements whose value is null should be output + * while serializing the configuration. + *

+ * The default value is {@code false}. + * + * @param outputNulls whether to output null values + * @return this builder + */ + public final B outputNulls(boolean outputNulls) { + this.outputNulls = outputNulls; + return getThis(); + } + + /** + * Sets whether fields or collection elements should allow null values to bet set + * while deserializing the configuration. + *

+ * The default value is {@code false}. + * + * @param inputNulls whether to input null values + * @return this builder + */ + public final B inputNulls(boolean inputNulls) { + this.inputNulls = inputNulls; + return getThis(); + } + + /** + * Sets whether sets should be serialized as lists. + *

+ * The default value is {@code true}. + * + * @param serializeSetsAsLists whether to serialize sets as lists + * @return this builder + */ + final B serializeSetsAsLists(boolean serializeSetsAsLists) { + this.serializeSetsAsLists = serializeSetsAsLists; + return getThis(); + } + + /** + * Builds a {@code ConfigurationProperties} instance. + * + * @return newly constructed {@code ConfigurationProperties} + */ + public abstract ConfigurationProperties build(); + + /** + * Returns this builder. + * + * @return this builder + */ + protected abstract B getThis(); + } + + /** + * Returns the field filter used to filter the fields of a configuration. + * + * @return the field filter + */ + public final FieldFilter getFieldFilter() { + return filter; + } + + /** + * Returns the field formatter used to format the fields of a configuration. + * + * @return the formatter + */ + public final FieldFormatter getFieldFormatter() { + return formatter; + } + + /** + * Returns an unmodifiable map of serializers by type. The serializers returned by this + * method take precedence over any default serializers provided by this library. + * + * @return serializers by type + */ + public final Map, Serializer> getSerializers() { + return serializersByType; + } + + /** + * Returns whether null values should be output. + * + * @return whether to output null values + */ + public final boolean outputNulls() { + return outputNulls; + } + + /** + * Returns whether null values should be allowed as input. + * + * @return whether to input null values + */ + public final boolean inputNulls() { + return inputNulls; + } + + + /** + * Returns whether sets should be serialized as lists. + * + * @return whether to serialize sets as lists + */ + final boolean serializeSetsAsLists() { + return serializeSetsAsLists; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/ConfigurationSerializer.java b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationSerializer.java new file mode 100644 index 0000000..63efe67 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/ConfigurationSerializer.java @@ -0,0 +1,139 @@ +package de.exlll.configlib; + +import java.lang.reflect.Field; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +final class ConfigurationSerializer implements Serializer> { + private final Class configurationType; + private final ConfigurationProperties properties; + private final Map> serializers; + + ConfigurationSerializer(Class configurationType, ConfigurationProperties properties) { + this.configurationType = Validator.requireConfiguration(configurationType); + this.properties = properties; + this.serializers = buildSerializerMap(); + requireSerializableFields(); + } + + private void requireSerializableFields() { + if (serializers.isEmpty()) { + String msg = "Configuration class '" + configurationType.getSimpleName() + "' " + + "does not contain any (de-)serializable fields."; + throw new ConfigurationException(msg); + } + } + + @Override + public Map serialize(T element) { + final Map result = new LinkedHashMap<>(); + + for (final Field field : filterFields()) { + final Object fieldValue = Reflect.getValue(field, element); + + if ((fieldValue == null) && !properties.outputNulls()) + continue; + + final Object serializedValue = serialize(field, fieldValue); + + final String formattedField = properties.getFieldFormatter().format(field); + result.put(formattedField, serializedValue); + } + + return result; + } + + private Object serialize(Field field, Object fieldValue) { + // The following cast won't cause a ClassCastException because we select the + // serializers based on the field type. + @SuppressWarnings("unchecked") + final Serializer serializer = + (Serializer) serializers.get(field.getName()); + + return (fieldValue != null) ? serializer.serialize(fieldValue) : null; + } + + @Override + public T deserialize(Map element) { + final T result = Reflect.newInstance(configurationType); + for (final Field field : filterFields()) { + final String formattedField = properties.getFieldFormatter().format(field); + + if (!element.containsKey(formattedField)) + continue; + + final Object value = element.get(formattedField); + + if (value == null && properties.inputNulls()) { + requireNonPrimitiveFieldType(field); + Reflect.setValue(field, result, null); + } else if (value != null) { + final Object deserialized = deserialize(field, value); + Reflect.setValue(field, result, deserialized); + } + } + return result; + } + + private Object deserialize(Field field, Object value) { + // This unchecked cast leads to an exception if the type of the object which + // is deserialized is not a subtype of the type the deserializer expects. + @SuppressWarnings("unchecked") + final Serializer serializer = + (Serializer) serializers.get(field.getName()); + + final Object deserialized; + try { + deserialized = serializer.deserialize(value); + } catch (ClassCastException e) { + String msg = baseDeserializeExceptionMessage(field, value) + "\n" + + "The type of the object to be deserialized does not " + + "match the type the deserializer expects."; + throw new ConfigurationException(msg, e); + } catch (RuntimeException e) { + String msg = baseDeserializeExceptionMessage(field, value); + throw new ConfigurationException(msg, e); + } + return deserialized; + } + + private static String baseDeserializeExceptionMessage(Field field, Object value) { + return "Deserialization of value '" + value + "' with type " + value.getClass() + " " + + "for field " + field + " failed."; + } + + private static void requireNonPrimitiveFieldType(Field field) { + if (field.getType().isPrimitive()) { + String msg = "Cannot set " + field + " to null value.\n" + + "Primitive types cannot be assigned null."; + throw new ConfigurationException(msg); + } + } + + private List filterFields() { + return FieldExtractors.CONFIGURATION.extract(configurationType) + .filter(properties.getFieldFilter()) + .toList(); + } + + Map> buildSerializerMap() { + SerializerSelector selector = new SerializerSelector(properties); + try { + return filterFields().stream() + .collect(Collectors.toMap( + Field::getName, + field -> selector.select(field.getGenericType()), + (serializer1, serializer2) -> serializer2 + )); + } catch (StackOverflowError error) { + String msg = "Recursive type definitions are not supported."; + throw new ConfigurationException(msg, error); + } + } + + Class getConfigurationType() { + return configurationType; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/Configurations.java b/configlib-core/src/main/java/de/exlll/configlib/Configurations.java new file mode 100644 index 0000000..8c07323 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/Configurations.java @@ -0,0 +1,236 @@ +package de.exlll.configlib; + +import java.nio.file.Path; +import java.util.function.Consumer; + +/** + * This class contains convenience methods for loading, saving, and updating configurations. + */ +public final class Configurations { + private Configurations() {} + + /** + * Loads a configuration of the given type from the specified YAML file using a + * {@code YamlConfigurationProperties} object with default values. + * + * @param configurationFile the file the configuration is loaded from + * @param configurationType the type of configuration + * @param the configuration type + * @return a newly created configuration initialized with values taken from the configuration file + * @throws ConfigurationException if the configuration cannot be deserialized + * @throws IllegalArgumentException if the file does not exist or is not a regular file + * @throws NullPointerException if any parameter is null + * @throws RuntimeException if reading the configuration throws an exception + * @see YamlConfigurationStore#load(Path) + */ + public static T loadYamlConfiguration( + Path configurationFile, + Class configurationType + ) { + final var properties = YamlConfigurationProperties.newBuilder().build(); + return loadYamlConfiguration(configurationFile, configurationType, properties); + } + + /** + * Loads a configuration of the given type from the specified YAML file using a + * {@code YamlConfigurationProperties} object that is built by a builder. The builder is + * initialized with default values and can be configured by the {@code propertiesConfigurer}. + * + * @param configurationFile the file the configuration is loaded from + * @param configurationType the type of configuration + * @param propertiesConfigurer the consumer used to configure the builder + * @param the configuration type + * @return a newly created configuration initialized with values taken from the configuration file + * @throws ConfigurationException if the configuration cannot be deserialized + * @throws IllegalArgumentException if the file does not exist or is not a regular file + * @throws NullPointerException if any parameter is null + * @throws RuntimeException if reading the configuration throws an exception + * @see YamlConfigurationStore#load(Path) + */ + public static T loadYamlConfiguration( + Path configurationFile, + Class configurationType, + Consumer> propertiesConfigurer + ) { + final var builder = YamlConfigurationProperties.newBuilder(); + propertiesConfigurer.accept(builder); + return loadYamlConfiguration(configurationFile, configurationType, builder.build()); + } + + /** + * Loads a configuration of the given type from the specified YAML file using the given + * {@code YamlConfigurationProperties} object. + * + * @param configurationFile the file the configuration is loaded from + * @param configurationType the type of configuration + * @param properties the configuration properties + * @param the configuration type + * @return a newly created configuration initialized with values taken from the configuration file + * @throws ConfigurationException if the configuration cannot be deserialized + * @throws IllegalArgumentException if the file does not exist or is not a regular file + * @throws NullPointerException if any parameter is null + * @throws RuntimeException if reading the configuration throws an exception + * @see YamlConfigurationStore#load(Path) + */ + public static T loadYamlConfiguration( + Path configurationFile, + Class configurationType, + YamlConfigurationProperties properties + ) { + final var store = new YamlConfigurationStore<>(configurationType, properties); + return store.load(configurationFile); + } + + /** + * Updates a YAML configuration file with a configuration of the given type using a + * {@code YamlConfigurationProperties} object with default values. + *

+ * See {@link YamlConfigurationStore#save(Object, Path)} for an explanation of how the update is + * done. + * + * @param configurationFile the configuration file that is updated + * @param configurationType the type of configuration + * @param the configuration type + * @return a newly created configuration initialized with values taken from the configuration file + * @throws ConfigurationException if the configuration cannot be deserialized + * @throws NullPointerException if any argument is null + * @throws RuntimeException if loading or saving the configuration throws an exception + * @see YamlConfigurationStore#update(Path) + */ + public static T updateYamlConfiguration( + Path configurationFile, + Class configurationType + ) { + final var properties = YamlConfigurationProperties.newBuilder().build(); + return updateYamlConfiguration(configurationFile, configurationType, properties); + } + + /** + * Updates a YAML configuration file with a configuration of the given type using a + * {@code YamlConfigurationProperties} object that is built by a builder. The builder is + * initialized with default values and can be configured by the {@code propertiesConfigurer}. + *

+ * See {@link YamlConfigurationStore#save(Object, Path)} for an explanation of how the update is + * done. + * + * @param configurationFile the configuration file that is updated + * @param configurationType the type of configuration + * @param propertiesConfigurer the consumer used to configure the builder + * @param the configuration type + * @return a newly created configuration initialized with values taken from the configuration file + * @throws ConfigurationException if the configuration cannot be deserialized + * @throws NullPointerException if any argument is null + * @throws RuntimeException if loading or saving the configuration throws an exception + * @see YamlConfigurationStore#update(Path) + */ + public static T updateYamlConfiguration( + Path configurationFile, + Class configurationType, + Consumer> propertiesConfigurer + ) { + final var builder = YamlConfigurationProperties.newBuilder(); + propertiesConfigurer.accept(builder); + return updateYamlConfiguration(configurationFile, configurationType, builder.build()); + } + + /** + * Updates a YAML configuration file with a configuration of the given type using the given + * {@code YamlConfigurationProperties} object. + *

+ * See {@link YamlConfigurationStore#save(Object, Path)} for an explanation of how the update is + * done. + * + * @param configurationFile the configuration file that is updated + * @param configurationType the type of configuration + * @param properties the configuration properties + * @param the configuration type + * @return a newly created configuration initialized with values taken from the configuration file + * @throws ConfigurationException if the configuration cannot be deserialized + * @throws NullPointerException if any argument is null + * @throws RuntimeException if loading or saving the configuration throws an exception + * @see YamlConfigurationStore#update(Path) + */ + public static T updateYamlConfiguration( + Path configurationFile, + Class configurationType, + YamlConfigurationProperties properties + ) { + final var store = new YamlConfigurationStore<>(configurationType, properties); + return store.update(configurationFile); + } + + /** + * Saves a configuration of the given type to the specified YAML file using a + * {@code YamlConfigurationProperties} object with default values. + * + * @param configuration the configuration that is saved + * @param configurationType the type of configuration + * @param configurationFile the file the configuration is saved to + * @param the configuration type + * @throws ConfigurationException if the configuration contains invalid values or + * cannot be serialized + * @throws NullPointerException if any argument is null + * @throws RuntimeException if writing the configuration throws an exception + * @see YamlConfigurationStore#save(Object, Path) + */ + public static void saveYamlConfiguration( + Path configurationFile, + Class configurationType, + T configuration + ) { + final var properties = YamlConfigurationProperties.newBuilder().build(); + saveYamlConfiguration(configurationFile, configurationType, configuration, properties); + } + + /** + * Saves a configuration of the given type to the specified YAML file using a + * {@code YamlConfigurationProperties} object that is built by a builder. The builder is + * initialized with default values and can be configured by the {@code propertiesConfigurer}. + * + * @param configuration the configuration that is saved + * @param configurationType the type of configuration + * @param configurationFile the file the configuration is saved to + * @param propertiesConfigurer the consumer used to configure the builder + * @param the configuration type + * @throws ConfigurationException if the configuration contains invalid values or + * cannot be serialized + * @throws NullPointerException if any argument is null + * @throws RuntimeException if writing the configuration throws an exception + * @see YamlConfigurationStore#save(Object, Path) + */ + public static void saveYamlConfiguration( + Path configurationFile, + Class configurationType, + T configuration, + Consumer> propertiesConfigurer + ) { + final var builder = YamlConfigurationProperties.newBuilder(); + propertiesConfigurer.accept(builder); + saveYamlConfiguration(configurationFile, configurationType, configuration, builder.build()); + } + + /** + * Saves a configuration of the given type to the specified YAML file using the given + * {@code YamlConfigurationProperties} object. + * + * @param configuration the configuration that is saved + * @param configurationType the type of configuration + * @param configurationFile the file the configuration is saved to + * @param properties the configuration properties + * @param the configuration type + * @throws ConfigurationException if the configuration contains invalid values or + * cannot be serialized + * @throws NullPointerException if any argument is null + * @throws RuntimeException if writing the configuration throws an exception + * @see YamlConfigurationStore#save(Object, Path) + */ + public static void saveYamlConfiguration( + Path configurationFile, + Class configurationType, + T configuration, + YamlConfigurationProperties properties + ) { + final var store = new YamlConfigurationStore<>(configurationType, properties); + store.save(configuration, configurationFile); + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/FieldExtractor.java b/configlib-core/src/main/java/de/exlll/configlib/FieldExtractor.java new file mode 100644 index 0000000..34f9a46 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/FieldExtractor.java @@ -0,0 +1,25 @@ +package de.exlll.configlib; + +import java.lang.reflect.Field; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Implementations of this interface extract the fields of classes. + */ +@FunctionalInterface +interface FieldExtractor extends Function, Stream> { + /** + * Extracts the fields of a class. + * + * @param cls the class + * @return a stream of fields + * @throws NullPointerException if {@code cls} is null + */ + Stream extract(Class cls); + + @Override + default Stream apply(Class cls) { + return extract(cls); + } +} \ No newline at end of file diff --git a/configlib-core/src/main/java/de/exlll/configlib/FieldExtractors.java b/configlib-core/src/main/java/de/exlll/configlib/FieldExtractors.java new file mode 100644 index 0000000..57a4d7a --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/FieldExtractors.java @@ -0,0 +1,59 @@ +package de.exlll.configlib; + +import java.lang.reflect.Field; +import java.util.*; +import java.util.function.Predicate; +import java.util.stream.Stream; + +enum FieldExtractors implements FieldExtractor { + /** + * Extracts the declared fields of the given configuration and all its super classes up to the + * first class that is not a configuration. + *

+ * The order of the fields is reversed such that the field of super classes are listed first. + */ + CONFIGURATION { + @Override + public Stream extract(Class cls) { + Validator.requireNonNull(cls, "configuration class"); + Validator.requireConfiguration(cls); + + List> classes = extractClassesWhile(cls, Reflect::isConfiguration); + List fields = classes.stream() + .flatMap(c -> Arrays.stream(c.getDeclaredFields())) + .filter(FieldFilters.DEFAULT) + .toList(); + requireNoShadowing(fields); + return fields.stream(); + } + }; + + private static void requireNoShadowing(List fields) { + Map> map = new LinkedHashMap<>(); + + for (Field field : fields) { + var fieldName = field.getName(); + var fieldClass = field.getDeclaringClass(); + + if (map.containsKey(fieldName)) { + Class superClass = map.get(fieldName); + String msg = "Shadowing of fields is not supported. Field '" + fieldName + "' " + + "of class " + fieldClass.getSimpleName() + " shadows field '" + + fieldName + "' of class " + superClass.getSimpleName() + "."; + throw new ConfigurationException(msg); + } + map.put(fieldName, fieldClass); + } + } + + private static List> extractClassesWhile(Class cls, Predicate> condition) { + List> classes = new ArrayList<>(); + Class current = cls; + while (condition.test(current)) { + classes.add(current); + current = current.getSuperclass(); + } + Collections.reverse(classes); // we want the fields of the super classes to come first + return classes; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/FieldFilter.java b/configlib-core/src/main/java/de/exlll/configlib/FieldFilter.java new file mode 100644 index 0000000..53af82d --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/FieldFilter.java @@ -0,0 +1,15 @@ +package de.exlll.configlib; + +import java.lang.reflect.Field; +import java.util.function.Predicate; + +/** + * Implementations of this interface test fields for specific conditions. + */ +@FunctionalInterface +public interface FieldFilter extends Predicate { + @Override + default FieldFilter and(Predicate other) { + return field -> test(field) && other.test(field); + } +} \ No newline at end of file diff --git a/configlib-core/src/main/java/de/exlll/configlib/FieldFilters.java b/configlib-core/src/main/java/de/exlll/configlib/FieldFilters.java new file mode 100644 index 0000000..35458b0 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/FieldFilters.java @@ -0,0 +1,27 @@ +package de.exlll.configlib; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +enum FieldFilters implements FieldFilter { + /** + * The default {@code FieldFilter} that rejects fields that are final, static, + * synthetic, transient, or annotated with {@code @Ignore}. + */ + DEFAULT { + @Override + public boolean test(Field field) { + Validator.requireNonNull(field, "field"); + + if (field.isSynthetic()) + return false; + if (Reflect.isIgnored(field)) + return false; + + int modifiers = field.getModifiers(); + return !Modifier.isFinal(modifiers) && + !Modifier.isStatic(modifiers) && + !Modifier.isTransient(modifiers); + } + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/FieldFormatter.java b/configlib-core/src/main/java/de/exlll/configlib/FieldFormatter.java new file mode 100644 index 0000000..5505471 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/FieldFormatter.java @@ -0,0 +1,24 @@ +package de.exlll.configlib; + +import java.lang.reflect.Field; +import java.util.function.Function; + +/** + * Implementations of this interface format the names of fields. + */ +@FunctionalInterface +public interface FieldFormatter extends Function { + /** + * Formats the name of the given field. + * + * @param field the field + * @return formatted field name + * @throws NullPointerException if {@code field} is null + */ + String format(Field field); + + @Override + default String apply(Field field) { + return format(field); + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/FieldFormatters.java b/configlib-core/src/main/java/de/exlll/configlib/FieldFormatters.java new file mode 100644 index 0000000..b3a3c20 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/FieldFormatters.java @@ -0,0 +1,59 @@ +package de.exlll.configlib; + +import java.lang.reflect.Field; + +/** + * This class contains instances of ready-to-use {@code FieldFormatter}s. + */ +public enum FieldFormatters implements FieldFormatter { + /** + * A {@code FieldFormatter} that returns the name of the field. + */ + IDENTITY { + @Override + public String format(Field field) { + return field.getName(); + } + }, + /** + * A {@code FieldFormatter} that transforms camelCase to + * lower_underscore. + *

+ * For example, myPrivateField becomes my_private_field. + */ + LOWER_UNDERSCORE { + @Override + public String format(Field field) { + String fn = field.getName(); + StringBuilder builder = new StringBuilder(fn.length()); + for (char c : fn.toCharArray()) { + if (Character.isUpperCase(c)) { + char lower = Character.toLowerCase(c); + builder.append('_').append(lower); + } else builder.append(c); + } + return builder.toString(); + } + }, + /** + * A {@code FieldFormatter} that transforms camelCase to + * UPPER_UNDERSCORE. + *

+ * For example, myPrivateField becomes MY_PRIVATE_FIELD. + */ + UPPER_UNDERSCORE { + @Override + public String format(Field field) { + String fn = field.getName(); + StringBuilder builder = new StringBuilder(fn.length()); + for (char c : fn.toCharArray()) { + if (Character.isLowerCase(c)) { + builder.append(Character.toUpperCase(c)); + } else if (Character.isUpperCase(c)) { + builder.append('_').append(c); + } else builder.append(c); + } + return builder.toString(); + } + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/FileConfigurationProperties.java b/configlib-core/src/main/java/de/exlll/configlib/FileConfigurationProperties.java new file mode 100644 index 0000000..4793b82 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/FileConfigurationProperties.java @@ -0,0 +1,137 @@ +package de.exlll.configlib; + +/** + * An extension of the {@code ConfigurationProperties} class that allows configuring properties + * that are more specific to files. + */ +public class FileConfigurationProperties extends ConfigurationProperties { + private final String header; + private final String footer; + private final boolean createParentDirectories; + + /** + * Constructs a new instance of this class with values taken from the given builder. + * + * @param builder the builder used to initialize the fields of this class + * @throws NullPointerException if the builder or any of its values is null + */ + protected FileConfigurationProperties(Builder builder) { + super(builder); + this.header = builder.header; + this.footer = builder.footer; + this.createParentDirectories = builder.createParentDirectories; + } + + /** + * Constructs a new {@code Builder} with default values. + * + * @return newly constructed {@code Builder} + */ + public static Builder newBuilder() { + return new BuilderImpl(); + } + + private static final class BuilderImpl extends Builder { + @Override + protected BuilderImpl getThis() {return this;} + + @Override + public FileConfigurationProperties build() {return new FileConfigurationProperties(this);} + } + + /** + * A builder class for constructing {@code FileConfigurationProperties}. + * + * @param the type of builder + */ + public static abstract class Builder> + extends ConfigurationProperties.Builder { + private String header = null; + private String footer = null; + private boolean createParentDirectories = true; + + /** + * A constructor that can be overridden by subclasses. + */ + protected Builder() {} + + /** + * Sets the header. The header is written as a comment before the actual configuration. + * If the header is set to null (the default), nothing is written. + * + * @param header the header + * @return this builder + */ + public final B header(String header) { + this.header = header; + return getThis(); + } + + /** + * Sets the footer. The footer is written as a comment after the actual configuration. + * If the footer is set to null (the default), nothing is written. + * + * @param footer the footer + * @return this builder + */ + public final B footer(String footer) { + this.footer = footer; + return getThis(); + } + + /** + * Sets whether parent directories of a configuration file should be created. + *

+ * The default value is {@code true}. + * + * @param createParentDirectories whether to create parent directories + * @return this builder + */ + public final B createParentDirectories(boolean createParentDirectories) { + this.createParentDirectories = createParentDirectories; + return getThis(); + } + + /** + * Builds a {@code ConfigurationProperties} instance. + * + * @return newly constructed {@code ConfigurationProperties} + */ + public abstract FileConfigurationProperties build(); + + /** + * Returns this builder. + * + * @return this builder + */ + protected abstract B getThis(); + } + + /** + * Returns the header. + * + * @return the header + */ + public final String getHeader() { + return header; + } + + /** + * Returns the footer. + * + * @return the footer + */ + public final String getFooter() { + return footer; + } + + + /** + * Returns whether to create parent directories. + * + * @return whether to create parent directories + */ + public final boolean createParentDirectories() { + return createParentDirectories; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/FileConfigurationStore.java b/configlib-core/src/main/java/de/exlll/configlib/FileConfigurationStore.java new file mode 100644 index 0000000..b24adb5 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/FileConfigurationStore.java @@ -0,0 +1,49 @@ +package de.exlll.configlib; + +import java.nio.file.Path; + +/** + * Instances of this class save and load configurations using files. + * + * @param the configuration type + */ +public interface FileConfigurationStore { + /** + * Saves a configuration instance to the given file. + * + * @param configuration the configuration + * @param configurationFile the file the configuration is saved to + * @throws ConfigurationException if the configuration contains invalid values or + * cannot be serialized + * @throws NullPointerException if any argument is null + * @throws RuntimeException if writing the configuration throws an exception + */ + void save(T configuration, Path configurationFile); + + /** + * Loads a configuration from the given file. + * + * @param configurationFile the file the configuration is loaded from + * @return a newly created configuration initialized with values taken from the configuration file + * @throws ConfigurationException if the configuration cannot be deserialized + * @throws IllegalArgumentException if the file does not exist or is not a regular file + * @throws NullPointerException if {@code configurationFile} is null + * @throws RuntimeException if reading the configuration throws an exception + */ + T load(Path configurationFile); + + /** + * Updates the configuration file. If the file does not exist, it is created and populated + * with the default values with which the fields of the configuration have been initialized. + * Otherwise, a new configuration instance is created, initialized with the values taken from + * the configuration file, and immediately saved to reflect possible changes of the + * configuration type. + * + * @param configurationFile the configuration file that is updated + * @return a newly created configuration initialized with values taken from the configuration file + * @throws ConfigurationException if the configuration cannot be deserialized + * @throws NullPointerException if {@code configurationFile} is null + * @throws RuntimeException if loading or saving the configuration throws an exception + */ + T update(Path configurationFile); +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/Ignore.java b/configlib-core/src/main/java/de/exlll/configlib/Ignore.java new file mode 100644 index 0000000..df95991 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/Ignore.java @@ -0,0 +1,13 @@ +package de.exlll.configlib; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that the annotated field should not be serialized. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Ignore {} diff --git a/configlib-core/src/main/java/de/exlll/configlib/Reflect.java b/configlib-core/src/main/java/de/exlll/configlib/Reflect.java new file mode 100644 index 0000000..a0938d4 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/Reflect.java @@ -0,0 +1,110 @@ +package de.exlll.configlib; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.Map; +import java.util.Set; + +final class Reflect { + private Reflect() {} + + static T newInstance(Class cls) { + try { + Constructor constructor = cls.getDeclaredConstructor(); + constructor.setAccessible(true); + return constructor.newInstance(); + } catch (NoSuchMethodException e) { + String msg = "Class " + cls.getSimpleName() + " doesn't have a " + + "no-args constructor."; + throw new RuntimeException(msg, e); + } catch (IllegalAccessException e) { + /* This exception should not be thrown because + * we set the constructor to be accessible. */ + String msg = "No-args constructor of class " + cls.getSimpleName() + + " not accessible."; + throw new RuntimeException(msg, e); + } catch (InstantiationException e) { + String msg = "Class " + cls.getSimpleName() + " is not instantiable."; + throw new RuntimeException(msg, e); + } catch (InvocationTargetException e) { + String msg = "Constructor of class " + cls.getSimpleName() + " threw an exception."; + throw new RuntimeException(msg, e); + } + } + + static T[] newArray(Class componentType, int length) { + // The following cast won't fail because we just created an array of that type + @SuppressWarnings("unchecked") + T[] array = (T[]) Array.newInstance(componentType, length); + return array; + } + + static Object getValue(Field field, Object instance) { + try { + field.setAccessible(true); + return field.get(instance); + } catch (IllegalAccessException e) { + /* This exception should not be thrown because + * we set the field to be accessible. */ + String msg = "Illegal access of field '" + field + "' " + + "on object " + instance + "."; + throw new RuntimeException(msg, e); + } + } + + static void setValue(Field field, Object instance, Object value) { + try { + field.setAccessible(true); + field.set(instance, value); + } catch (IllegalAccessException e) { + /* This exception should not be thrown because + * we set the field to be accessible. */ + String msg = "Illegal access of field '" + field + "' " + + "on object " + instance + "."; + throw new RuntimeException(msg, e); + } + } + + static boolean isIntegerType(Class cls) { + return (cls == byte.class) || (cls == Byte.class) || + (cls == short.class) || (cls == Short.class) || + (cls == int.class) || (cls == Integer.class) || + (cls == long.class) || (cls == Long.class); + } + + static boolean isFloatingPointType(Class cls) { + return (cls == float.class) || (cls == Float.class) || + (cls == double.class) || (cls == Double.class); + } + + static boolean isEnumType(Class cls) { + return cls.isEnum(); + } + + static boolean isArrayType(Class cls) { + return cls.isArray(); + } + + static boolean isListType(Class cls) { + return List.class.isAssignableFrom(cls); + } + + static boolean isSetType(Class cls) { + return Set.class.isAssignableFrom(cls); + } + + static boolean isMapType(Class cls) { + return Map.class.isAssignableFrom(cls); + } + + static boolean isConfiguration(Class cls) { + return cls.getAnnotation(Configuration.class) != null; + } + + static boolean isIgnored(Field field) { + return field.getAnnotation(Ignore.class) != null; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/Serializer.java b/configlib-core/src/main/java/de/exlll/configlib/Serializer.java new file mode 100644 index 0000000..5209a16 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/Serializer.java @@ -0,0 +1,42 @@ +package de.exlll.configlib; + +/** + * Implementations of this interface convert instances of type {@code T1} to a serializable type + * {@code T2} and vice versa. + *

+ * Which types {@code T2} are serializable depends on the underlying storage system. Currently, + * all storage systems support the following types: + *

    + *
  • {@code Boolean}
  • + *
  • {@code Long}
  • + *
  • {@code Double}
  • + *
  • {@code String}
  • + *
  • (Nested) {@code List}s of the other types
  • + *
  • (Nested) {@code Set}s of the other types
  • + *
  • (Nested) {@code Map}s of the other types
  • + *
+ *

+ * That means that if you want to support all currently available configuration store formats, + * your {@code Serializer} implementation should convert an object of type {@code T1} into one + * of the seven types listed above. + * + * @param the type of the objects that should be serialized + * @param the serializable type + */ +public interface Serializer { + /** + * Serializes an element of type {@code T1} into an element of type {@code T2}. + * + * @param element the element of type {@code T1} that is serialized + * @return the serialized element of type {@code T2} + */ + T2 serialize(T1 element); + + /** + * Deserializes an element of type {@code T2} into an element of type {@code T1}. + * + * @param element the element of type {@code T2} that is deserialized + * @return the deserialized element of type {@code T1} + */ + T1 deserialize(T2 element); +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java b/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java new file mode 100644 index 0000000..9d2b4b6 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/SerializerSelector.java @@ -0,0 +1,145 @@ +package de.exlll.configlib; + +import de.exlll.configlib.Serializers.*; + +import java.lang.reflect.*; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Map; + +final class SerializerSelector { + private static final Map, Serializer> DEFAULT_SERIALIZERS = Map.ofEntries( + Map.entry(boolean.class, new BooleanSerializer()), + Map.entry(Boolean.class, new BooleanSerializer()), + Map.entry(byte.class, new NumberSerializer(byte.class)), + Map.entry(Byte.class, new NumberSerializer(Byte.class)), + Map.entry(short.class, new NumberSerializer(short.class)), + Map.entry(Short.class, new NumberSerializer(Short.class)), + Map.entry(int.class, new NumberSerializer(int.class)), + Map.entry(Integer.class, new NumberSerializer(Integer.class)), + Map.entry(long.class, new NumberSerializer(long.class)), + Map.entry(Long.class, new NumberSerializer(Long.class)), + Map.entry(float.class, new NumberSerializer(float.class)), + Map.entry(Float.class, new NumberSerializer(Float.class)), + Map.entry(double.class, new NumberSerializer(double.class)), + Map.entry(Double.class, new NumberSerializer(Double.class)), + Map.entry(char.class, new CharacterSerializer()), + Map.entry(Character.class, new CharacterSerializer()), + Map.entry(String.class, new StringSerializer()), + Map.entry(BigInteger.class, new BigIntegerSerializer()), + Map.entry(BigDecimal.class, new BigDecimalSerializer()), + Map.entry(LocalDate.class, new LocalDateSerializer()), + Map.entry(LocalTime.class, new LocalTimeSerializer()), + Map.entry(LocalDateTime.class, new LocalDateTimeSerializer()) + ); + private final ConfigurationProperties properties; + + public SerializerSelector(ConfigurationProperties properties) { + this.properties = properties; + } + + public Serializer select(Type type) { + if (type instanceof Class cls) { + return selectForClass(cls); + } else if (type instanceof ParameterizedType pType) { + return selectForParameterizedType(pType); + } else if (type instanceof WildcardType) { + String msg = baseExceptionMessage(type) + "Wildcard types cannot be serialized."; + throw new ConfigurationException(msg); + } else if (type instanceof GenericArrayType) { + String msg = baseExceptionMessage(type) + "Generic array types cannot be serialized."; + throw new ConfigurationException(msg); + } else if (type instanceof TypeVariable) { + String msg = baseExceptionMessage(type) + "Type variables cannot be serialized."; + throw new ConfigurationException(msg); + } + // should not happen as we covered all possible types + throw new ConfigurationException(baseExceptionMessage(type)); + } + + private Serializer selectForClass(Class cls) { + if (properties.getSerializers().containsKey(cls)) + return properties.getSerializers().get(cls); + if (DEFAULT_SERIALIZERS.containsKey(cls)) + return DEFAULT_SERIALIZERS.get(cls); + if (Reflect.isEnumType(cls)) { + // The following cast won't fail because we just checked that it's an enum. + @SuppressWarnings("unchecked") + var enumCls = (Class>) cls; + return new Serializers.EnumSerializer(enumCls); + } + if (Reflect.isArrayType(cls)) + return selectForArray(cls.getComponentType()); + if (Reflect.isConfiguration(cls)) + return new ConfigurationSerializer<>(cls, properties); + + String msg = "Missing serializer for type " + cls + ".\n" + + "Either annotate the type with @Configuration or provide a custom " + + "serializer by adding it to the properties."; + throw new ConfigurationException(msg); + } + + private Serializer selectForArray(Class elementType) { + if (elementType == boolean.class) { + return new PrimitiveBooleanArraySerializer(); + } else if (elementType == char.class) { + return new PrimitiveCharacterArraySerializer(); + } else if (elementType == byte.class) { + return new PrimitiveByteArraySerializer(); + } else if (elementType == short.class) { + return new PrimitiveShortArraySerializer(); + } else if (elementType == int.class) { + return new PrimitiveIntegerArraySerializer(); + } else if (elementType == long.class) { + return new PrimitiveLongArraySerializer(); + } else if (elementType == float.class) { + return new PrimitiveFloatArraySerializer(); + } else if (elementType == double.class) { + return new PrimitiveDoubleArraySerializer(); + } + var elementSerializer = select(elementType); + var inputNulls = properties.inputNulls(); + var outputNulls = properties.outputNulls(); + return new ArraySerializer<>(elementType, elementSerializer, outputNulls, inputNulls); + } + + private Serializer selectForParameterizedType(ParameterizedType type) { + // the raw type returned by Java is always a class + final var rawType = (Class) type.getRawType(); + final var typeArgs = type.getActualTypeArguments(); + final var inputNulls = properties.inputNulls(); + final var outputNulls = properties.outputNulls(); + + if (Reflect.isListType(rawType)) { + var elementSerializer = select(typeArgs[0]); + return new ListSerializer<>(elementSerializer, outputNulls, inputNulls); + } else if (Reflect.isSetType(rawType)) { + var elementSerializer = select(typeArgs[0]); + return properties.serializeSetsAsLists() + ? new SetAsListSerializer<>(elementSerializer, outputNulls, inputNulls) + : new SetSerializer<>(elementSerializer, outputNulls, inputNulls); + } else if (Reflect.isMapType(rawType)) { + if ((typeArgs[0] instanceof Class cls) && + (DEFAULT_SERIALIZERS.containsKey(cls) || + Reflect.isEnumType(cls))) { + var keySerializer = select(typeArgs[0]); + var valSerializer = select(typeArgs[1]); + return new MapSerializer<>(keySerializer, valSerializer, outputNulls, inputNulls); + } + String msg = baseExceptionMessage(type) + + "Map keys can only be of simple or enum type."; + throw new ConfigurationException(msg); + } + + String msg = baseExceptionMessage(type) + + "Parameterized types other than lists, sets, and maps cannot be serialized."; + throw new ConfigurationException(msg); + } + + private String baseExceptionMessage(Type type) { + return "Cannot select serializer for type '" + type + "'.\n"; + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/Serializers.java b/configlib-core/src/main/java/de/exlll/configlib/Serializers.java new file mode 100644 index 0000000..2eb9e61 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/Serializers.java @@ -0,0 +1,621 @@ +package de.exlll.configlib; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.function.IntFunction; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +final class Serializers { + private Serializers() {} + + static final class BooleanSerializer implements Serializer { + @Override + public Boolean serialize(Boolean element) { + return element; + } + + @Override + public Boolean deserialize(Boolean element) { + return element; + } + } + + /** + * Converts a primitive number or its wrapper type to another number type according to the + * following rules: + *

    + *
  • + * The {@code serialize} method converts all four integer types to {@code Long}, and + * both floating point types to {@code Double}. + *
  • + *
  • + * The {@code deserialize} method converts a number from any supported type to the + * requested type if the value of the number fits in the range. + *
  • + *
+ */ + static final class NumberSerializer implements Serializer { + private final Class cls; + + public NumberSerializer(Class cls) { + this.cls = Validator.requireNonNull(cls, "number class"); + Validator.requirePrimitiveOrWrapperNumberType(cls); + } + + @Override + public Number serialize(Number element) { + if (Reflect.isIntegerType(cls)) + return element.longValue(); + if (Reflect.isFloatingPointType(cls)) + return element.doubleValue(); + String msg = "Invalid element '" + element + "' with type " + element.getClass(); + throw new ConfigurationException(msg); // should not happen, types checked in ctor + } + + @Override + public Number deserialize(Number element) { + if (Reflect.isIntegerType(element.getClass())) { + return deserializeFromIntegerType(element); + } + if (Reflect.isFloatingPointType(element.getClass())) { + return deserializeFromFloatingPointType(element); + } + String clsName = element.getClass().getSimpleName(); + String msg = "Cannot deserialize element '" + element + "' of type " + clsName + ".\n" + + "This serializer only supports primitive number types and their wrapper types."; + throw new ConfigurationException(msg); + } + + private Number deserializeFromFloatingPointType(Number element) { + double value = element.doubleValue(); + + if (cls == float.class || cls == Float.class) { + if (Double.isNaN(value)) + return Float.NaN; + if (value == Double.POSITIVE_INFINITY) + return Float.POSITIVE_INFINITY; + if (value == Double.NEGATIVE_INFINITY) + return Float.NEGATIVE_INFINITY; + if (value != 0.0) + requireFloatingPointInRange(value); + return element.floatValue(); + } + + return value; + } + + private Number deserializeFromIntegerType(Number element) { + long value = element.longValue(); + + if (cls == byte.class || cls == Byte.class) { + requireIntegerInRange(value, Byte.MIN_VALUE, Byte.MAX_VALUE); + return element.byteValue(); + } + + if (cls == short.class || cls == Short.class) { + requireIntegerInRange(value, Short.MIN_VALUE, Short.MAX_VALUE); + return element.shortValue(); + } + + if (cls == int.class || cls == Integer.class) { + requireIntegerInRange(value, Integer.MIN_VALUE, Integer.MAX_VALUE); + return element.intValue(); + } + + return value; + } + + private void requireIntegerInRange(long value, long low, long high) { + if (value < low || value > high) { + String msg = baseExceptionMessage(value) + "It does not fit into the range " + + "of valid values [" + low + ", " + high + "]."; + throw new ConfigurationException(msg); + } + } + + private void requireFloatingPointInRange(double value) { + final String clsName = cls.getSimpleName(); + if ((value > -Float.MIN_VALUE) && (value < Float.MIN_VALUE)) { + String msg = baseExceptionMessage(value) + "It is smaller than the smallest " + + "possible " + clsName + " value."; + throw new ConfigurationException(msg); + } + if ((value < -Float.MAX_VALUE) || (value > Float.MAX_VALUE)) { + String msg = baseExceptionMessage(value) + "It is larger than the largest " + + "possible " + clsName + " value."; + throw new ConfigurationException(msg); + } + } + + private String baseExceptionMessage(T value) { + String clsName = cls.getSimpleName(); + return "Number " + value + " cannot be converted to type " + clsName + ". "; + } + + public Class getNumberClass() { + return cls; + } + } + + static final class StringSerializer implements Serializer { + @Override + public String serialize(String element) { + return element; + } + + @Override + public String deserialize(String element) { + return element; + } + } + + static final class CharacterSerializer implements Serializer { + @Override + public String serialize(Character element) { + return element.toString(); + } + + @Override + public Character deserialize(String element) { + int length = element.length(); + + if (length == 0) { + String msg = "An empty string cannot be converted to a character."; + throw new ConfigurationException(msg); + } + if (length > 1) { + String msg = "String '" + element + "' is too long to be converted " + + "to a character."; + throw new ConfigurationException(msg); + } + + return element.charAt(0); + } + } + + static final class BigIntegerSerializer implements Serializer { + @Override + public String serialize(BigInteger element) { + return element.toString(); + } + + @Override + public BigInteger deserialize(String element) { + return new BigInteger(element); + } + } + + static final class BigDecimalSerializer implements Serializer { + @Override + public String serialize(BigDecimal element) { + return element.toString(); + } + + @Override + public BigDecimal deserialize(String element) { + return new BigDecimal(element); + } + } + + static final class LocalTimeSerializer implements Serializer { + @Override + public String serialize(LocalTime element) { + return element.truncatedTo(ChronoUnit.SECONDS).toString(); + } + + @Override + public LocalTime deserialize(String element) { + return LocalTime.parse(element); + } + } + + static final class LocalDateSerializer implements Serializer { + @Override + public String serialize(LocalDate element) { + return element.toString(); + } + + @Override + public LocalDate deserialize(String element) { + return LocalDate.parse(element); + } + } + + static final class LocalDateTimeSerializer implements Serializer { + @Override + public String serialize(LocalDateTime element) { + return element.truncatedTo(ChronoUnit.SECONDS).toString(); + } + + @Override + public LocalDateTime deserialize(String element) { + return LocalDateTime.parse(element); + } + } + + static final class EnumSerializer implements Serializer, String> { + private final Class> cls; + + public EnumSerializer(Class> cls) { + this.cls = Validator.requireNonNull(cls, "enum class"); + } + + @Override + public String serialize(Enum element) { + return element.name(); + } + + @Override + public Enum deserialize(String element) { + for (Enum constant : cls.getEnumConstants()) { + if (constant.name().equals(element)) { + return constant; + } + } + String msg = createExceptionMessage(element); + throw new ConfigurationException(msg); + } + + private String createExceptionMessage(String element) { + String enums = Arrays.stream(cls.getEnumConstants()) + .map(Enum::name) + .collect(Collectors.joining(", ", "[", "]")); + return "Enum class " + cls.getSimpleName() + " does not contain enum '" + + element + "'. Valid values are: " + enums; + } + + public Class> getEnumCls() { + return cls; + } + } + + static class CollectionSerializer, R extends Collection> + implements Serializer { + private final Serializer serializer; + private final boolean outputNulls; + private final boolean inputNulls; + private final Supplier lSupplier; + private final Supplier rSupplier; + + public CollectionSerializer( + Serializer serializer, + boolean outputNulls, + boolean inputNulls, + Supplier lSupplier, + Supplier rSupplier + ) { + this.serializer = Validator.requireNonNull(serializer, "element serializer"); + this.outputNulls = outputNulls; + this.inputNulls = inputNulls; + this.lSupplier = lSupplier; + this.rSupplier = rSupplier; + } + + @Override + public final R serialize(L element) { + final Stream stream = outputNulls + ? element.stream().map(s -> s == null ? null : serializer.serialize(s)) + : element.stream().filter(Objects::nonNull).map(serializer::serialize); + return stream.collect(Collectors.toCollection(rSupplier)); + } + + @Override + public final L deserialize(R element) { + final Stream stream = inputNulls + ? element.stream().map(t -> t == null ? null : serializer.deserialize(t)) + : element.stream().filter(Objects::nonNull).map(serializer::deserialize); + return stream.collect(Collectors.toCollection(lSupplier)); + } + + public final Serializer getElementSerializer() { + return serializer; + } + } + + static final class ListSerializer extends CollectionSerializer, List> { + public ListSerializer(Serializer serializer, boolean outputNulls, boolean inputNulls) { + super(serializer, outputNulls, inputNulls, ArrayList::new, ArrayList::new); + } + } + + static final class SetSerializer extends CollectionSerializer, Set> { + public SetSerializer(Serializer serializer, boolean outputNulls, boolean inputNulls) { + super(serializer, outputNulls, inputNulls, HashSet::new, LinkedHashSet::new); + } + } + + static final class SetAsListSerializer extends CollectionSerializer, List> { + public SetAsListSerializer(Serializer serializer, boolean outputNulls, boolean inputNulls) { + super(serializer, outputNulls, inputNulls, HashSet::new, ArrayList::new); + } + } + + static final class MapSerializer implements Serializer, Map> { + private final Serializer keySerializer; + private final Serializer valSerializer; + private final boolean outputNulls; + private final boolean inputNulls; + + public MapSerializer( + Serializer keySerializer, + Serializer valSerializer, + boolean outputNulls, + boolean inputNulls + ) { + this.keySerializer = Validator.requireNonNull(keySerializer, "key serializer"); + this.valSerializer = Validator.requireNonNull(valSerializer, "value serializer"); + this.outputNulls = outputNulls; + this.inputNulls = inputNulls; + } + + @Override + public Map serialize(Map element) { + // cannot work with Collectors.toMap as is doesn't allow null values + final Map result = new LinkedHashMap<>(); + for (final Map.Entry entry : element.entrySet()) { + if (!outputNulls && isEntryNull(entry)) + continue; + var s1key = entry.getKey(); + var s2val = entry.getValue(); + var t1key = (s1key == null) ? null : keySerializer.serialize(s1key); + var t2val = (s2val == null) ? null : valSerializer.serialize(s2val); + result.put(t1key, t2val); + } + return result; + } + + @Override + public Map deserialize(Map element) { + // cannot work with Collectors.toMap as is doesn't allow null values + final Map result = new LinkedHashMap<>(); + for (final Map.Entry entry : element.entrySet()) { + if (!inputNulls && isEntryNull(entry)) + continue; + var t1key = entry.getKey(); + var t2val = entry.getValue(); + var s1key = (t1key == null) ? null : keySerializer.deserialize(t1key); + var s2val = (t2val == null) ? null : valSerializer.deserialize(t2val); + result.put(s1key, s2val); + } + return result; + } + + private static boolean isEntryNull(Map.Entry entry) { + return (entry == null) || (entry.getKey() == null) || (entry.getValue() == null); + } + + public Serializer getKeySerializer() { + return keySerializer; + } + + public Serializer getValueSerializer() { + return valSerializer; + } + } + + static final class ArraySerializer implements Serializer> { + private final Class componentType; + private final Serializer serializer; + private final boolean outputNulls; + private final boolean inputNulls; + + public ArraySerializer( + Class componentType, + Serializer serializer, + boolean outputNulls, + boolean inputNulls + ) { + this.componentType = Validator.requireNonNull(componentType, "component type"); + this.serializer = Validator.requireNonNull(serializer, "element serializer"); + this.outputNulls = outputNulls; + this.inputNulls = inputNulls; + } + + @Override + public List serialize(T1[] element) { + final Stream stream = outputNulls + ? Arrays.stream(element).map(s -> s == null ? null : serializer.serialize(s)) + : Arrays.stream(element).filter(Objects::nonNull).map(serializer::serialize); + return stream.toList(); + } + + @Override + public T1[] deserialize(List element) { + final Stream stream = inputNulls + ? element.stream().map(t -> t == null ? null : serializer.deserialize(t)) + : element.stream().filter(Objects::nonNull).map(serializer::deserialize); + // The following cast won't fail because we choose the elementSerializer based + // on the componentType. + @SuppressWarnings("unchecked") + IntFunction f = value -> (T1[]) Reflect.newArray(componentType, value); + return stream.toArray(f); + } + + public Class getComponentType() { + return componentType; + } + + public Serializer getElementSerializer() { + return serializer; + } + } + + static final class PrimitiveBooleanArraySerializer implements Serializer> { + @Override + public List serialize(Object element) { + final boolean[] array = (boolean[]) element; + return IntStream.range(0, array.length).mapToObj(i -> array[i]).toList(); + } + + @Override + public Object deserialize(List element) { + final boolean[] array = new boolean[element.size()]; + for (int i = 0; i < element.size(); i++) + array[i] = Validator.requireNonNullArrayElement(element.get(i), "boolean", i); + return array; + } + } + + static final class PrimitiveCharacterArraySerializer + implements Serializer> { + private static final CharacterSerializer serializer = new CharacterSerializer(); + + @Override + public List serialize(Object element) { + final char[] array = (char[]) element; + return IntStream.range(0, array.length) + .mapToObj(i -> serializer.serialize(array[i])) + .toList(); + } + + @Override + public Object deserialize(List element) { + final char[] array = new char[element.size()]; + for (int i = 0; i < element.size(); i++) { + String character = Validator.requireNonNullArrayElement(element.get(i), "char", i); + array[i] = serializer.deserialize(character); + } + return array; + } + } + + static final class PrimitiveByteArraySerializer + implements Serializer> { + private static final NumberSerializer serializer = new NumberSerializer(byte.class); + + @Override + public List serialize(Object element) { + final byte[] array = (byte[]) element; + return IntStream.range(0, array.length). + mapToObj(i -> serializer.serialize(array[i])) + .toList(); + } + + @Override + public Object deserialize(List element) { + final byte[] array = new byte[element.size()]; + for (int i = 0; i < element.size(); i++) { + Number number = Validator.requireNonNullArrayElement(element.get(i), "byte", i); + array[i] = (byte) serializer.deserialize(number); + } + return array; + } + } + + static final class PrimitiveShortArraySerializer + implements Serializer> { + private static final NumberSerializer serializer = new NumberSerializer(short.class); + + @Override + public List serialize(Object element) { + final short[] array = (short[]) element; + return IntStream.range(0, array.length) + .mapToObj(i -> serializer.serialize(array[i])) + .toList(); + } + + @Override + public Object deserialize(List element) { + final short[] array = new short[element.size()]; + for (int i = 0; i < element.size(); i++) { + Number number = Validator.requireNonNullArrayElement(element.get(i), "short", i); + array[i] = (short) serializer.deserialize(number); + } + return array; + } + } + + static final class PrimitiveIntegerArraySerializer + implements Serializer> { + private static final NumberSerializer serializer = new NumberSerializer(int.class); + + @Override + public List serialize(Object element) { + final int[] array = (int[]) element; + return Arrays.stream(array).mapToObj(serializer::serialize).toList(); + } + + @Override + public Object deserialize(List element) { + final int[] array = new int[element.size()]; + for (int i = 0; i < element.size(); i++) { + Number number = Validator.requireNonNullArrayElement(element.get(i), "int", i); + array[i] = (int) serializer.deserialize(number); + } + return array; + } + } + + static final class PrimitiveLongArraySerializer + implements Serializer> { + private static final NumberSerializer serializer = new NumberSerializer(long.class); + + @Override + public List serialize(Object element) { + final long[] array = (long[]) element; + return Arrays.stream(array).mapToObj(serializer::serialize).toList(); + } + + @Override + public Object deserialize(List element) { + final long[] array = new long[element.size()]; + for (int i = 0; i < element.size(); i++) { + Number number = Validator.requireNonNullArrayElement(element.get(i), "long", i); + array[i] = (long) serializer.deserialize(number); + } + return array; + } + } + + static final class PrimitiveFloatArraySerializer + implements Serializer> { + private static final NumberSerializer serializer = new NumberSerializer(float.class); + + @Override + public List serialize(Object element) { + final float[] array = (float[]) element; + return IntStream.range(0, array.length) + .mapToObj(i -> serializer.serialize(array[i])) + .toList(); + } + + @Override + public Object deserialize(List element) { + final float[] array = new float[element.size()]; + for (int i = 0; i < element.size(); i++) { + Number number = Validator.requireNonNullArrayElement(element.get(i), "float", i); + array[i] = (float) serializer.deserialize(number); + } + return array; + } + } + + static final class PrimitiveDoubleArraySerializer + implements Serializer> { + private static final NumberSerializer serializer = new NumberSerializer(double.class); + + @Override + public List serialize(Object element) { + final double[] array = (double[]) element; + return Arrays.stream(array).mapToObj(serializer::serialize).toList(); + } + + @Override + public Object deserialize(List element) { + final double[] array = new double[element.size()]; + for (int i = 0; i < element.size(); i++) { + Number number = Validator.requireNonNullArrayElement(element.get(i), "double", i); + array[i] = (double) serializer.deserialize(number); + } + return array; + } + } +} diff --git a/configlib-core/src/main/java/de/exlll/configlib/Validator.java b/configlib-core/src/main/java/de/exlll/configlib/Validator.java new file mode 100644 index 0000000..8a2ea14 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/Validator.java @@ -0,0 +1,36 @@ +package de.exlll.configlib; + +import java.util.Objects; + +final class Validator { + private Validator() {} + + static T requireNonNull(T object, String argumentName) { + String msg = "The " + argumentName + " must not be null."; + return Objects.requireNonNull(object, msg); + } + + static T requireNonNullArrayElement(T element, String type, int index) { + if (element == null) { + String msg = "The " + type + " element at index " + index + " must not be null."; + throw new ConfigurationException(msg); + } + return element; + } + + static Class requireConfiguration(Class cls) { + if (!Reflect.isConfiguration(cls)) { + String msg = "Class '" + cls.getSimpleName() + "' must be a configuration."; + throw new ConfigurationException(msg); + } + return cls; + } + + static void requirePrimitiveOrWrapperNumberType(Class cls) { + if (!Reflect.isIntegerType(cls) && !Reflect.isFloatingPointType(cls)) { + String msg = "Class " + cls.getSimpleName() + " is not a byte, short, int, long, " + + "float, double, or a wrapper type of one of the primitive number types."; + throw new IllegalArgumentException(msg); + } + } +} \ No newline at end of file diff --git a/configlib-core/src/main/java/de/exlll/configlib/YamlConfigurationProperties.java b/configlib-core/src/main/java/de/exlll/configlib/YamlConfigurationProperties.java new file mode 100644 index 0000000..93504bf --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/YamlConfigurationProperties.java @@ -0,0 +1,62 @@ +package de.exlll.configlib; + +/** + * An extension of the {@code FileConfigurationProperties} class that allows configuring properties + * that are more specific to YAML files. + */ +public final class YamlConfigurationProperties extends FileConfigurationProperties { + /** + * Constructs a new instance of this class with values taken from the given builder. + * + * @param builder the builder used to initialize the fields of this class + * @throws NullPointerException if the builder or any of its values is null + */ + public YamlConfigurationProperties(Builder builder) { + super(builder); + } + + /** + * Constructs a new {@code Builder} with default values. + * + * @return newly constructed {@code Builder} + */ + public static Builder newBuilder() { + return new BuilderImpl(); + } + + private static final class BuilderImpl extends Builder { + @Override + protected BuilderImpl getThis() {return this;} + + @Override + public YamlConfigurationProperties build() {return new YamlConfigurationProperties(this);} + } + + /** + * A builder class for constructing {@code YamlConfigurationProperties}. + * + * @param the type of builder + */ + public static abstract class Builder> + extends FileConfigurationProperties.Builder { + + /** + * A constructor that can be overridden by subclasses. + */ + protected Builder() {} + + /** + * Builds a {@code ConfigurationProperties} instance. + * + * @return newly constructed {@code ConfigurationProperties} + */ + public abstract YamlConfigurationProperties build(); + + /** + * Returns this builder. + * + * @return this builder + */ + protected abstract B getThis(); + } +} \ No newline at end of file diff --git a/configlib-core/src/main/java/de/exlll/configlib/YamlConfigurationStore.java b/configlib-core/src/main/java/de/exlll/configlib/YamlConfigurationStore.java new file mode 100644 index 0000000..91d13e0 --- /dev/null +++ b/configlib-core/src/main/java/de/exlll/configlib/YamlConfigurationStore.java @@ -0,0 +1,286 @@ +package de.exlll.configlib; + +import org.snakeyaml.engine.v2.api.Dump; +import org.snakeyaml.engine.v2.api.DumpSettings; +import org.snakeyaml.engine.v2.api.Load; +import org.snakeyaml.engine.v2.api.LoadSettings; +import org.snakeyaml.engine.v2.common.FlowStyle; +import org.snakeyaml.engine.v2.exceptions.YamlEngineException; +import org.snakeyaml.engine.v2.nodes.Node; +import org.snakeyaml.engine.v2.nodes.Tag; +import org.snakeyaml.engine.v2.representer.StandardRepresenter; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +import static de.exlll.configlib.Validator.requireNonNull; + +/** + * A configuration store that saves and loads configurations as YAML text files. + * + * @param the configuration type + */ +public final class YamlConfigurationStore implements FileConfigurationStore { + private static final Dump YAML_DUMPER = newYamlDumper(); + private static final Load YAML_LOADER = newYamlLoader(); + private final Class configurationType; + private final YamlConfigurationProperties properties; + private final ConfigurationSerializer serializer; + private final CommentNodeExtractor extractor; + + /** + * Constructs a new store. + * + * @param configurationType the type of configuration + * @param properties the properties + * @throws NullPointerException if any argument is null + */ + public YamlConfigurationStore(Class configurationType, YamlConfigurationProperties properties) { + this.configurationType = requireNonNull(configurationType, "configuration type"); + this.properties = requireNonNull(properties, "properties"); + this.serializer = new ConfigurationSerializer<>(configurationType, properties); + this.extractor = new CommentNodeExtractor(properties); + } + + @Override + public void save(T configuration, Path configurationFile) { + tryCreateParentDirectories(configurationFile); + var extractedCommentNodes = extractor.extractCommentNodes(configuration); + var yamlFileWriter = new YamlFileWriter(configurationFile, properties); + var dumpedYaml = tryDump(configuration); + yamlFileWriter.writeYaml(dumpedYaml, extractedCommentNodes); + } + + private void tryCreateParentDirectories(Path configurationFile) { + Path parent = configurationFile.getParent(); + if (!Files.exists(parent) && properties.createParentDirectories()) { + try { + Files.createDirectories(parent); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + private String tryDump(T configuration) { + final Map serializedConfiguration = serializer.serialize(configuration); + try { + return YAML_DUMPER.dumpToString(serializedConfiguration); + } catch (YamlEngineException e) { + String msg = "The given configuration could not be converted into YAML. \n" + + "Do all custom serializers produce valid target types?"; + throw new ConfigurationException(msg, e); + } + } + + @Override + public T load(Path configurationFile) { + try (var reader = Files.newBufferedReader(configurationFile)) { + var yaml = YAML_LOADER.loadFromReader(reader); + var conf = requireConfiguration(yaml, configurationFile); + return serializer.deserialize(conf); + } catch (YamlEngineException e) { + String msg = "The configuration file at %s does not contain valid YAML."; + throw new ConfigurationException(msg.formatted(configurationFile), e); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private Map requireConfiguration(Object yaml, Path configurationFile) { + if (yaml == null) { + String msg = "The configuration file at %s is empty or only contains null."; + throw new ConfigurationException(msg.formatted(configurationFile)); + } + + if (!(yaml instanceof Map)) { + String msg = "The contents of the YAML file at %s do not represent a configuration. " + + "A valid configuration file contains a YAML map but instead a " + + "'" + yaml.getClass() + "' was found."; + throw new ConfigurationException(msg.formatted(configurationFile)); + } + + return (Map) yaml; + } + + @Override + public T update(Path configurationFile) { + if (Files.exists(configurationFile)) { + T configuration = load(configurationFile); + save(configuration, configurationFile); + return configuration; + } + T configuration = Reflect.newInstance(configurationType); + save(configuration, configurationFile); + return configuration; + } + + static Dump newYamlDumper() { + DumpSettings settings = DumpSettings.builder() + .setDefaultFlowStyle(FlowStyle.BLOCK) + .setIndent(2) + .build(); + return new Dump(settings, new YamlConfigurationRepresenter(settings)); + } + + static Load newYamlLoader() { + LoadSettings settings = LoadSettings.builder().build(); + return new Load(settings); + } + + /** + * A writer that writes YAML to a file. + */ + static final class YamlFileWriter { + private final Path configurationFile; + private final YamlConfigurationProperties properties; + private BufferedWriter writer; + + YamlFileWriter(Path configurationFile, YamlConfigurationProperties properties) { + this.configurationFile = requireNonNull(configurationFile, "configuration file"); + this.properties = requireNonNull(properties, "configuration properties"); + } + + public void writeYaml(String yaml, Queue nodes) { + try (BufferedWriter writer = Files.newBufferedWriter(configurationFile)) { + this.writer = writer; + writeHeader(); + writeContent(yaml, nodes); + writeFooter(); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + this.writer = null; + } + } + + private void writeHeader() throws IOException { + if (properties.getHeader() != null) { + writeAsComment(properties.getHeader()); + writer.newLine(); + } + } + + private void writeFooter() throws IOException { + if (properties.getFooter() != null) { + writer.newLine(); + writeAsComment(properties.getFooter()); + } + } + + private void writeAsComment(String comment) throws IOException { + String[] lines = comment.split("\n"); + writeComments(Arrays.asList(lines), 0); + } + + private void writeComments(List comments, int indentLevel) throws IOException { + String indent = " ".repeat(indentLevel); + for (String comment : comments) { + if (comment.isEmpty()) { + writer.newLine(); + continue; + } + String line = indent + "# " + comment; + writeLine(line); + } + } + + private void writeLine(String line) throws IOException { + writer.write(line); + writer.newLine(); + } + + private void writeContent(String yaml, Queue nodes) throws IOException { + if (nodes.isEmpty()) { + writer.write(yaml); + } else { + writeCommentedYaml(yaml, nodes); + } + } + + private void writeCommentedYaml(String yaml, Queue nodes) + throws IOException { + /* + * The following algorithm is necessary since no Java YAML library seems + * to properly support comments, at least not the way I want them. + * + * The algorithm writes YAML line by line and keeps track of the current + * context with the help of fieldNames lists which come from the nodes in + * the 'nodes' queue. The 'nodes' queue contains nodes in the order in + * which the fields were read, which happened in DFS manner and with fields + * of a parent class being read before the fields of the child. That order + * ultimately represents the order in which the YAML file is structured. + */ + var node = nodes.poll(); + var currentIndentLevel = 0; + + for (final String line : yaml.split("\n")) { + if (node == null) { + writeLine(line); + continue; + } + + final var fieldNames = node.fieldNames(); + final var indent = " ".repeat(currentIndentLevel); + + final var lineStart = indent + fieldNames.get(currentIndentLevel) + ":"; + if (!line.startsWith(lineStart)) { + writeLine(line); + continue; + } + + final var commentIndentLevel = fieldNames.size() - 1; + if (currentIndentLevel++ == commentIndentLevel) { + writeComments(node.comments(), commentIndentLevel); + if ((node = nodes.poll()) != null) { + currentIndentLevel = lengthCommonPrefix(node.fieldNames(), fieldNames); + } + } + + writeLine(line); + } + } + + static int lengthCommonPrefix(List l1, List l2) { + final int maxLen = Math.min(l1.size(), l2.size()); + int result = 0; + for (int i = 0; i < maxLen; i++) { + String s1 = l1.get(i); + String s2 = l2.get(i); + if (s1.equals(s2)) + result++; + else return result; + } + return result; + } + } + + /** + * A custom representer that prevents aliasing. + */ + static final class YamlConfigurationRepresenter extends StandardRepresenter { + public YamlConfigurationRepresenter(DumpSettings settings) { + super(settings); + } + + @Override + protected Node representSequence(Tag tag, Iterable sequence, FlowStyle flowStyle) { + Node node = super.representSequence(tag, sequence, flowStyle); + representedObjects.clear(); + return node; + } + + @Override + protected Node representMapping(Tag tag, Map mapping, FlowStyle flowStyle) { + Node node = super.representMapping(tag, mapping, flowStyle); + representedObjects.clear(); + return node; + } + } +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/CommentNodeExtractorTest.java b/configlib-core/src/test/java/de/exlll/configlib/CommentNodeExtractorTest.java new file mode 100644 index 0000000..a971682 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/CommentNodeExtractorTest.java @@ -0,0 +1,322 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +import static de.exlll.configlib.TestUtils.assertThrowsConfigurationException; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SuppressWarnings({"unused", "FieldMayBeFinal"}) +class CommentNodeExtractorTest { + private static final ConfigurationProperties PROPERTIES = ConfigurationProperties.newBuilder() + .outputNulls(true) + .build(); + + private final CommentNodeExtractor EXTRACTOR = new CommentNodeExtractor(PROPERTIES); + + @Test + void requiresConfiguration() { + assertThrowsConfigurationException( + () -> EXTRACTOR.extractCommentNodes(new Object()), + "Class 'Object' must be a configuration." + ); + } + + @Test + void extractSingleComment1() { + @Configuration + class A { + @Comment("Hello") + int i; + } + + Queue nodes = EXTRACTOR.extractCommentNodes(new A()); + assertEquals(cn(List.of("Hello"), "i"), nodes.poll()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractSingleComment2() { + @Configuration + class A { + @Comment({"Hello", "World"}) + int i; + } + + Queue nodes = EXTRACTOR.extractCommentNodes(new A()); + assertEquals(cn(List.of("Hello", "World"), "i"), nodes.poll()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractMultipleComments() { + @Configuration + class A { + @Comment("Hello") + int i; + int j; + @Comment("World") + int k; + } + + Queue nodes = EXTRACTOR.extractCommentNodes(new A()); + assertEquals(cn(List.of("Hello"), "i"), nodes.poll()); + assertEquals(cn(List.of("World"), "k"), nodes.poll()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractNestedComment1() { + @Configuration + class A { + @Comment("Hello") + int i; + } + @Configuration + class B { + A a = new A(); + } + Queue nodes = EXTRACTOR.extractCommentNodes(new B()); + assertEquals(cn(List.of("Hello"), "a", "i"), nodes.poll()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractNestedComment2() { + @Configuration + class A { + @Comment("Hello") + int i; + int j; + @Comment("World") + int k; + } + @Configuration + class B { + A a = new A(); + } + Queue nodes = EXTRACTOR.extractCommentNodes(new B()); + assertEquals(cn(List.of("Hello"), "a", "i"), nodes.poll()); + assertEquals(cn(List.of("World"), "a", "k"), nodes.poll()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractNestedComment3() { + @Configuration + class A { + @Comment("Hello") + int i; + int j; + @Comment("World") + int k; + } + @Configuration + class B { + @Comment("Hello") + A a1 = new A(); + @Comment("World") + A a2 = new A(); + } + @Configuration + class C { + @Comment("Hello") + B b = new B(); + @Comment("World") + A a = new A(); + } + @Configuration + class D { + C c = new C(); + } + Queue nodes = EXTRACTOR.extractCommentNodes(new D()); + assertEquals(cn(List.of("Hello"), "c", "b"), nodes.poll()); + assertEquals(cn(List.of("Hello"), "c", "b", "a1"), nodes.poll()); + assertEquals(cn(List.of("Hello"), "c", "b", "a1", "i"), nodes.poll()); + assertEquals(cn(List.of("World"), "c", "b", "a1", "k"), nodes.poll()); + assertEquals(cn(List.of("World"), "c", "b", "a2"), nodes.poll()); + assertEquals(cn(List.of("Hello"), "c", "b", "a2", "i"), nodes.poll()); + assertEquals(cn(List.of("World"), "c", "b", "a2", "k"), nodes.poll()); + assertEquals(cn(List.of("World"), "c", "a"), nodes.poll()); + assertEquals(cn(List.of("Hello"), "c", "a", "i"), nodes.poll()); + assertEquals(cn(List.of("World"), "c", "a", "k"), nodes.poll()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractEmptyClass() { + @Configuration + class A {} + Queue nodes = EXTRACTOR.extractCommentNodes(new A()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractNestedOnlyIfNotNull() { + @Configuration + class A { + @Comment("Hello") + int i; + } + @Configuration + class B { + A a = null; + } + @Configuration + class C { + @Comment("Hello") + A a = null; + } + Queue nodes1 = EXTRACTOR.extractCommentNodes(new B()); + assertTrue(nodes1.isEmpty()); + + Queue nodes2 = EXTRACTOR.extractCommentNodes(new C()); + assertEquals(cn(List.of("Hello"), "a"), nodes2.poll()); + assertTrue(nodes2.isEmpty()); + } + + @Test + void extractIgnoresCommentIfFieldNullAndOutputNull1() { + @Configuration + class A { + @Comment("Hello") + int i; + } + @Configuration + class B { + A a = null; + } + @Configuration + class C { + @Comment("Hello") + A a = null; + } + ConfigurationProperties properties = ConfigurationProperties.newBuilder() + .outputNulls(false) + .build(); + CommentNodeExtractor extractor = new CommentNodeExtractor(properties); + + Queue nodes1 = extractor.extractCommentNodes(new B()); + assertTrue(nodes1.isEmpty()); + + Queue nodes2 = extractor.extractCommentNodes(new C()); + assertTrue(nodes2.isEmpty()); + } + + @Test + void extractIgnoresCommentIfFieldNullAndOutputNull2() { + @Configuration + class A { + @Comment("Hello") + String s1 = null; + @Comment("World") + String s2 = ""; + } + ConfigurationProperties properties = ConfigurationProperties.newBuilder() + .outputNulls(false) + .build(); + CommentNodeExtractor extractor = new CommentNodeExtractor(properties); + Queue nodes = extractor.extractCommentNodes(new A()); + assertEquals(cn(List.of("World"), "s2"), nodes.poll()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractAppliesFormatterAndFilter() { + @Configuration + class A { + @Comment("Hello") + int i; + int j; + @Comment("World") + int k; + } + @Configuration + class B { + @Comment("Hello") + A a1 = new A(); + @Comment("World") + A a2 = new A(); + } + @Configuration + class C { + @Comment("Hello") + B b = new B(); + @Comment("World") + A a = new A(); + } + + ConfigurationProperties properties = ConfigurationProperties.newBuilder() + .setFieldFilter(field -> { + String name = field.getName(); + return !name.equals("a1") && !name.equals("a"); + }) + .setFieldFormatter(FieldFormatters.UPPER_UNDERSCORE) + .build(); + + CommentNodeExtractor extractor = new CommentNodeExtractor(properties); + Queue nodes = extractor.extractCommentNodes(new C()); + assertEquals(cn(List.of("Hello"), "B"), nodes.poll()); + assertEquals(cn(List.of("World"), "B", "A2"), nodes.poll()); + assertEquals(cn(List.of("Hello"), "B", "A2", "I"), nodes.poll()); + assertEquals(cn(List.of("World"), "B", "A2", "K"), nodes.poll()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractIgnoresCommentsForConfigurationsInCollections() { + @Configuration + class A { + @Comment("Yes") + int i; + } + @Configuration + class B { + Set
setA; + List listA; + Map mapIntegerA; + } + Queue nodes = EXTRACTOR.extractCommentNodes(new B()); + assertTrue(nodes.isEmpty()); + } + + @Test + void extractCommentsFromParentClasses() { + @Configuration + class A1 { + @Comment({"int", "i"}) + int i; + } + @Configuration + class A2 extends A1 { + @Comment({"int", "j"}) + int j; + } + @Configuration + class B1 { + @Comment({"A2", "a2"}) + A2 a2 = new A2(); + } + @Configuration + class B2 extends B1 { + @Comment({"A1", "a1"}) + A1 a1 = new A1(); + } + Queue nodes = EXTRACTOR.extractCommentNodes(new B2()); + assertEquals(cn(List.of("A2", "a2"), "a2"), nodes.poll()); + assertEquals(cn(List.of("int", "i"), "a2", "i"), nodes.poll()); + assertEquals(cn(List.of("int", "j"), "a2", "j"), nodes.poll()); + assertEquals(cn(List.of("A1", "a1"), "a1"), nodes.poll()); + assertEquals(cn(List.of("int", "i"), "a1", "i"), nodes.poll()); + assertTrue(nodes.isEmpty()); + } + + private static CommentNode cn(List comments, String... fieldNames) { + return new CommentNode(comments, List.of(fieldNames)); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/ConfigurationPropertiesTest.java b/configlib-core/src/test/java/de/exlll/configlib/ConfigurationPropertiesTest.java new file mode 100644 index 0000000..43c3bcf --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/ConfigurationPropertiesTest.java @@ -0,0 +1,94 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.Test; + +import java.awt.Point; +import java.util.Locale; +import java.util.Map; + +import static de.exlll.configlib.TestUtils.assertThrowsNullPointerException; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ConfigurationPropertiesTest { + + @Test + void builderDefaultValues() { + ConfigurationProperties properties = ConfigurationProperties.newBuilder().build(); + + assertThat(properties.serializeSetsAsLists(), is(true)); + assertThat(properties.outputNulls(), is(false)); + assertThat(properties.inputNulls(), is(false)); + assertThat(properties.getSerializers().entrySet(), empty()); + assertThat(properties.getFieldFormatter(), is(FieldFormatters.IDENTITY)); + assertThat(properties.getFieldFilter(), is(FieldFilters.DEFAULT)); + } + + @Test + void builderCopiesValues() { + FieldFormatter formatter = field -> field.getName().toLowerCase(Locale.ROOT); + FieldFilter filter = field -> field.getName().startsWith("f"); + TestUtils.PointSerializer serializer = new TestUtils.PointSerializer(); + + ConfigurationProperties properties = ConfigurationProperties.newBuilder() + .addSerializer(Point.class, serializer) + .setFieldFormatter(formatter) + .setFieldFilter(filter) + .outputNulls(true) + .inputNulls(true) + .serializeSetsAsLists(false) + .build(); + + assertThat(properties.getSerializers(), is(Map.of(Point.class, serializer))); + assertThat(properties.outputNulls(), is(true)); + assertThat(properties.inputNulls(), is(true)); + assertThat(properties.serializeSetsAsLists(), is(false)); + assertThat(properties.getFieldFormatter(), sameInstance(formatter)); + assertThat(properties.getFieldFilter(), sameInstance(filter)); + } + + @Test + void builderSerializersUnmodifiable() { + ConfigurationProperties properties = ConfigurationProperties.newBuilder().build(); + Map, Serializer> map = properties.getSerializers(); + + assertThrows( + UnsupportedOperationException.class, + () -> map.put(Point.class, new TestUtils.PointSerializer()) + ); + } + + public static final class BuilderTest { + private static final ConfigurationProperties.Builder builder = ConfigurationProperties.newBuilder(); + + @Test + void setFieldFilterRequiresNonNull() { + assertThrowsNullPointerException( + () -> builder.setFieldFilter(null), + "field filter" + ); + } + + @Test + void setFieldFormatterRequiresNonNull() { + assertThrowsNullPointerException( + () -> builder.setFieldFormatter(null), + "field formatter" + ); + } + + @Test + void addSerializerRequiresNonNull() { + assertThrowsNullPointerException( + () -> builder.addSerializer(null, new Serializers.StringSerializer()), + "serialized type" + ); + + assertThrowsNullPointerException( + () -> builder.addSerializer(String.class, null), + "serializer" + ); + } + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/ConfigurationSerializerTest.java b/configlib-core/src/test/java/de/exlll/configlib/ConfigurationSerializerTest.java new file mode 100644 index 0000000..70f13f0 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/ConfigurationSerializerTest.java @@ -0,0 +1,317 @@ +package de.exlll.configlib; + +import de.exlll.configlib.ConfigurationProperties.Builder; +import de.exlll.configlib.Serializers.*; +import de.exlll.configlib.configurations.*; +import org.junit.jupiter.api.Test; + +import java.awt.Point; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import static de.exlll.configlib.TestUtils.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; + +@SuppressWarnings("FieldMayBeFinal") +class ConfigurationSerializerTest { + private static ConfigurationSerializer newSerializer(Class cls) { + return newSerializer(cls, builder -> builder); + } + + private static ConfigurationSerializer newSerializer( + Class cls, + Function, Builder> propertiesConfigurer + ) { + var builder = ConfigurationProperties.newBuilder() + .addSerializer(Point.class, TestUtils.POINT_SERIALIZER); + builder.addSerializer(Point.class, TestUtils.POINT_SERIALIZER); + builder = propertiesConfigurer.apply(builder); + return new ConfigurationSerializer<>(cls, builder.build()); + } + + @Test + void buildSerializerMapFiltersFields() { + Map> serializers = newSerializer(ExampleConfigurationA2.class) + .buildSerializerMap(); + + assertThat(serializers.get("a1_staticFinalInt"), nullValue()); + assertThat(serializers.get("a1_staticInt"), nullValue()); + assertThat(serializers.get("a1_finalInt"), nullValue()); + assertThat(serializers.get("a1_transientInt"), nullValue()); + assertThat(serializers.get("a1_ignoredInt"), nullValue()); + assertThat(serializers.get("a1_ignoredString"), nullValue()); + assertThat(serializers.get("a1_ignoredListString"), nullValue()); + + assertThat(serializers.get("a2_staticFinalInt"), nullValue()); + assertThat(serializers.get("a2_staticInt"), nullValue()); + assertThat(serializers.get("a2_finalInt"), nullValue()); + assertThat(serializers.get("a2_transientInt"), nullValue()); + assertThat(serializers.get("a2_ignoredInt"), nullValue()); + assertThat(serializers.get("a2_ignoredString"), nullValue()); + assertThat(serializers.get("a2_ignoredListString"), nullValue()); + } + + @Test + void buildSerializerMapIgnoresFormatter() { + Map> serializers = newSerializer( + ExampleConfigurationA2.class, + props -> props.setFieldFormatter(FieldFormatters.UPPER_UNDERSCORE) + ).buildSerializerMap(); + + assertThat(serializers.get("A2_PRIM_BOOL"), nullValue()); + assertThat(serializers.get("a2_primBool"), instanceOf(BooleanSerializer.class)); + } + + @Test + void buildSerializerMap() { + Map> serializers = newSerializer(ExampleConfigurationA2.class) + .buildSerializerMap(); + assertThat(serializers.get("a2_primBool"), instanceOf(BooleanSerializer.class)); + assertThat(serializers.get("a2_refChar"), instanceOf(CharacterSerializer.class)); + assertThat(serializers.get("a2_string"), instanceOf(StringSerializer.class)); + assertThat(serializers.get("a2_Enm"), instanceOf(EnumSerializer.class)); + + ConfigurationSerializer serializerB1 = + (ConfigurationSerializer) serializers.get("a2_b1"); + ConfigurationSerializer serializerB2 = + (ConfigurationSerializer) serializers.get("a2_b2"); + + assertThat(serializerB1.getConfigurationType(), equalTo(ExampleConfigurationB1.class)); + assertThat(serializerB2.getConfigurationType(), equalTo(ExampleConfigurationB2.class)); + + ListSerializer serializerList = + (ListSerializer) serializers.get("a2_listByte"); + ArraySerializer serializerArray = + (ArraySerializer) serializers.get("a2_arrayString"); + SetAsListSerializer serializerSet = + (SetAsListSerializer) serializers.get("a2_setBigInteger"); + MapSerializer serializerMap = + (MapSerializer) serializers.get("a2_mapLocalTimeLocalTime"); + + assertThat( + serializers.get("a2_arrayPrimDouble"), + instanceOf(PrimitiveDoubleArraySerializer.class) + ); + + assertThat(serializerList.getElementSerializer(), instanceOf(NumberSerializer.class)); + assertThat(serializerArray.getElementSerializer(), instanceOf(StringSerializer.class)); + assertThat(serializerSet.getElementSerializer(), instanceOf(BigIntegerSerializer.class)); + assertThat(serializerMap.getKeySerializer(), instanceOf(LocalTimeSerializer.class)); + assertThat(serializerMap.getValueSerializer(), instanceOf(LocalTimeSerializer.class)); + + assertThat(serializers.get("a2_point"), sameInstance(TestUtils.POINT_SERIALIZER)); + } + + @Test + void serializeAppliesFormatter() { + @Configuration + class A { + int value1 = 1; + int someValue2 = 2; + } + ConfigurationSerializer serializer = newSerializer( + A.class, + builder -> builder.setFieldFormatter(FieldFormatters.UPPER_UNDERSCORE) + ); + Map map = serializer.serialize(new A()); + assertThat(map.remove("VALUE1"), is(1L)); + assertThat(map.remove("SOME_VALUE2"), is(2L)); + assertTrue(map.isEmpty()); + } + + @Configuration + private static final class B1 { + int value1 = 1; + int someValue2 = 2; + } + + @Test + void deserializeAppliesFormatter() { + ConfigurationSerializer serializer = newSerializer( + B1.class, + builder -> builder.setFieldFormatter(FieldFormatters.UPPER_UNDERSCORE) + ); + Map map = Map.of( + "value1", 3, + "someValue2", 4, + "VALUE1", 5, + "SOME_VALUE2", 6 + ); + B1 a = serializer.deserialize(map); + assertThat(a.value1, is(5)); + assertThat(a.someValue2, is(6)); + } + + @Configuration + private static final class B2 { + boolean f1; + char f2; + byte f3; + short f4; + int f5; + long f6; + float f7; + double f8; + } + + @Test + void deserializeNullForPrimitiveFields() { + ConfigurationSerializer serializer = newSerializer( + B2.class, + builder -> builder.inputNulls(true) + ); + for (int i = 1; i <= 8; i++) { + String fieldName = "f" + i; + Map map = asMap(fieldName, null); + assertThrowsConfigurationException( + () -> serializer.deserialize(map), + "Cannot set " + getField(B2.class, fieldName) + " to null value.\n" + + "Primitive types cannot be assigned null." + ); + } + } + + @Configuration + private static final class B3 { + String s = ""; + List> l = List.of(); + } + + @Test + void deserializeInvalidType() { + ConfigurationSerializer serializer = newSerializer(B3.class); + assertThrowsConfigurationException( + () -> serializer.deserialize(Map.of("s", (byte) 3)), + "Deserialization of value '3' with type class java.lang.Byte for field " + + "java.lang.String de.exlll.configlib.ConfigurationSerializerTest$B3.s " + + "failed.\nThe type of the object to be deserialized does not match the type " + + "the deserializer expects." + ); + assertThrowsConfigurationException( + () -> serializer.deserialize(Map.of("l", List.of(List.of(3)))), + "Deserialization of value '[[3]]' with type class " + + "java.util.ImmutableCollections$List12 for field java.util.List " + + "de.exlll.configlib.ConfigurationSerializerTest$B3.l failed.\n" + + "The type of the object to be deserialized does not match the type the " + + "deserializer expects." + ); + } + + + @Configuration + private static final class B4 { + private static final ExampleConfigurationB1 B4_NULL_B1 = + ExampleInitializer.newExampleConfigurationB1_1(); + private static final List B4_NULL_LIST = List.of(); + private static final Double[] B4_NULL_ARRAY = new Double[0]; + private static final Set B4_NULL_SET = Set.of(); + private static final Map B4_NULL_MAP = Map.of(); + private static final Point B4_NULL_POINT = new Point(0, 0); + + Integer nullInteger = 1; + String nullString = ""; + ExampleEnum nullEnm = ExampleEnum.A; + ExampleConfigurationB1 nullB1 = B4_NULL_B1; + List nullList = B4_NULL_LIST; + Double[] nullArray = B4_NULL_ARRAY; + Set nullSet = B4_NULL_SET; + Map nullMap = B4_NULL_MAP; + Point nullPoint = B4_NULL_POINT; + } + + @Test + void deserializeWithoutNullDoesNotOverrideInitializedFields() { + final Map map = entriesAsMap( + entry("nullInteger", null), + entry("nullString", null), + entry("nullEnm", null), + entry("nullB1", null), + entry("nullList", null), + entry("nullArray", null), + entry("nullSet", null), + entry("nullMap", null), + entry("nullPoint", null) + ); + ConfigurationSerializer serializer = newSerializer(B4.class); + B4 config = serializer.deserialize(map); + + assertEquals(1, config.nullInteger); + assertEquals("", config.nullString); + assertEquals(ExampleEnum.A, config.nullEnm); + assertSame(B4.B4_NULL_B1, config.nullB1); + assertSame(B4.B4_NULL_LIST, config.nullList); + assertSame(B4.B4_NULL_ARRAY, config.nullArray); + assertSame(B4.B4_NULL_SET, config.nullSet); + assertSame(B4.B4_NULL_MAP, config.nullMap); + assertSame(B4.B4_NULL_POINT, config.nullPoint); + } + + @Configuration + private static final class B5 { + @Ignore + private int ignored = 1; + } + + @Test + void ctorRequiresConfigurationWithFields() { + assertThrowsConfigurationException( + () -> newSerializer(B5.class), + "Configuration class 'B5' does not contain any (de-)serializable fields." + ); + } + + private static final class B6 { + @Ignore + private int ignored = 1; + } + + @Test + void ctorRequiresConfiguration() { + assertThrowsConfigurationException( + () -> newSerializer(B6.class), + "Class 'B6' must be a configuration." + ); + } + + @Test + void buildSerializerMapPreventsRecursiveDefinitions() { + assertThrowsConfigurationException( + () -> newSerializer(R1.class), + "Recursive type definitions are not supported." + ); + } + + @Configuration + static final class R1 { + R2 r2; + } + + @Configuration + static final class R2 { + R1 r1; + } + + @Test + void serializeTypeWithAbstractParent() { + ConfigurationSerializer serializer = newSerializer(B8.class); + Map serialize = serializer.serialize(new B8()); + B8 deserialize = serializer.deserialize(serialize); + assertEquals(1, deserialize.i); + assertEquals(2, deserialize.j); + } + + @Configuration + static abstract class B7 { + int i = 1; + } + + static final class B8 extends B7 { + int j = 2; + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/ConfigurationsTest.java b/configlib-core/src/test/java/de/exlll/configlib/ConfigurationsTest.java new file mode 100644 index 0000000..709c71d --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/ConfigurationsTest.java @@ -0,0 +1,147 @@ +package de.exlll.configlib; + +import com.google.common.jimfs.Jimfs; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ConfigurationsTest { + private static final FieldFilter includeI = field -> field.getName().equals("i"); + private final FileSystem fs = Jimfs.newFileSystem(); + private final Path yamlFile = fs.getPath("/tmp/config.yml"); + + @BeforeEach + void setUp() throws IOException { + Files.createDirectories(yamlFile.getParent()); + } + + @AfterEach + void tearDown() throws IOException { + fs.close(); + } + + @Configuration + private static final class Config { + int i = 10; + int j = 11; + } + + @Test + void saveYamlConfiguration1() { + Config configuration = new Config(); + + Configurations.saveYamlConfiguration(yamlFile, Config.class, configuration); + assertEquals("i: 10\nj: 11", TestUtils.readFile(yamlFile)); + + configuration.i = 20; + Configurations.saveYamlConfiguration(yamlFile, Config.class, configuration); + assertEquals("i: 20\nj: 11", TestUtils.readFile(yamlFile)); + } + + @Test + void saveYamlConfiguration2() { + Config configuration = new Config(); + + Configurations.saveYamlConfiguration( + yamlFile, Config.class, configuration, + builder -> builder.setFieldFilter(includeI) + ); + assertEquals("i: 10", TestUtils.readFile(yamlFile)); + } + + @Test + void saveYamlConfiguration3() { + Config configuration = new Config(); + + Configurations.saveYamlConfiguration( + yamlFile, Config.class, configuration, + YamlConfigurationProperties.newBuilder().setFieldFilter(includeI).build() + ); + assertEquals("i: 10", TestUtils.readFile(yamlFile)); + } + + @Test + void loadYamlConfiguration1() { + writeString("i: 20\nk: 30"); + Config config = Configurations.loadYamlConfiguration(yamlFile, Config.class); + assertConfigEquals(config, 20, 11); + + writeString("i: 20\nj: 30"); + config = Configurations.loadYamlConfiguration(yamlFile, Config.class); + assertConfigEquals(config, 20, 30); + } + + @Test + void loadYamlConfiguration2() { + writeString("i: 20\nj: 30"); + Config config = Configurations.loadYamlConfiguration( + yamlFile, Config.class, + builder -> builder.setFieldFilter(includeI) + ); + assertConfigEquals(config, 20, 11); + } + + @Test + void loadYamlConfiguration3() { + writeString("i: 20\nj: 30"); + + Config config = Configurations.loadYamlConfiguration( + yamlFile, Config.class, + YamlConfigurationProperties.newBuilder().setFieldFilter(includeI).build() + ); + + assertConfigEquals(config, 20, 11); + } + + @Test + void updateYamlConfiguration1() { + Config config = Configurations.updateYamlConfiguration(yamlFile, Config.class); + assertConfigEquals(config, 10, 11); + assertEquals("i: 10\nj: 11", TestUtils.readFile(yamlFile)); + + writeString("i: 20\nk: 30"); + config = Configurations.updateYamlConfiguration(yamlFile, Config.class); + assertConfigEquals(config, 20, 11); + assertEquals("i: 20\nj: 11", TestUtils.readFile(yamlFile)); + } + + @Test + void updateYamlConfiguration2() { + Config config = Configurations.updateYamlConfiguration( + yamlFile, Config.class, + builder -> builder.setFieldFilter(includeI) + ); + assertConfigEquals(config, 10, 11); + assertEquals("i: 10", TestUtils.readFile(yamlFile)); + } + + @Test + void updateYamlConfiguration3() { + Config config = Configurations.updateYamlConfiguration( + yamlFile, Config.class, + YamlConfigurationProperties.newBuilder().setFieldFilter(includeI).build() + ); + assertConfigEquals(config, 10, 11); + assertEquals("i: 10", TestUtils.readFile(yamlFile)); + } + + private static void assertConfigEquals(Config config, int i, int j) { + assertEquals(i, config.i); + assertEquals(j, config.j); + } + + private void writeString(String string) { + try { + Files.writeString(yamlFile, string); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/ExampleConfigurationTests.java b/configlib-core/src/test/java/de/exlll/configlib/ExampleConfigurationTests.java new file mode 100644 index 0000000..ac67fe3 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/ExampleConfigurationTests.java @@ -0,0 +1,673 @@ +package de.exlll.configlib; + +import com.google.common.jimfs.Jimfs; +import de.exlll.configlib.configurations.*; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.awt.Point; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import static de.exlll.configlib.configurations.ExampleConfigurationsSerialized.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ExampleConfigurationTests { + private static final ConfigurationProperties PROPERTIES_ALLOW_NULL = ConfigurationProperties.newBuilder() + .addSerializer(Point.class, TestUtils.POINT_SERIALIZER) + .outputNulls(true) + .inputNulls(true) + .build(); + private static final ConfigurationProperties PROPERTIES_DENY_NULL = ConfigurationProperties.newBuilder() + .addSerializer(Point.class, TestUtils.POINT_SERIALIZER) + .outputNulls(false) + .inputNulls(false) + .build(); + + private final FileSystem fs = Jimfs.newFileSystem(); + private final Path yamlFile = fs.getPath("/tmp/config.yml"); + + @BeforeEach + void setUp() throws IOException { + Files.createDirectories(yamlFile.getParent()); + } + + @AfterEach + void tearDown() throws IOException { + fs.close(); + } + + @Test + void serializeExampleConfigurationB1() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationB1.class, PROPERTIES_ALLOW_NULL); + + ExampleConfigurationB1 config1 = ExampleInitializer.newExampleConfigurationB1_1(); + Map serialized1 = serializer.serialize(config1); + assertEquals(EXAMPLE_CONFIGURATION_B1_1, serialized1); + + ExampleConfigurationB1 config2 = ExampleInitializer.newExampleConfigurationB1_2(); + Map serialized2 = serializer.serialize(config2); + assertEquals(EXAMPLE_CONFIGURATION_B1_2, serialized2); + } + + @Test + void deserializeExampleConfigurationB1() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationB1.class, PROPERTIES_ALLOW_NULL); + + assertExampleConfigurationsB1Equal( + serializer.deserialize(EXAMPLE_CONFIGURATION_B1_1), + ExampleInitializer.newExampleConfigurationB1_1() + ); + + assertExampleConfigurationsB1Equal( + serializer.deserialize(EXAMPLE_CONFIGURATION_B1_2), + ExampleInitializer.newExampleConfigurationB1_2() + ); + } + + @Test + void serializeExampleConfigurationB2() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationB2.class, PROPERTIES_ALLOW_NULL); + + ExampleConfigurationB2 config1 = ExampleInitializer.newExampleConfigurationB2_1(); + Map serialized1 = serializer.serialize(config1); + assertEquals(EXAMPLE_CONFIGURATION_B2_1, serialized1); + + ExampleConfigurationB2 config2 = ExampleInitializer.newExampleConfigurationB2_2(); + Map serialized2 = serializer.serialize(config2); + assertEquals(EXAMPLE_CONFIGURATION_B2_2, serialized2); + } + + @Test + void deserializeExampleConfigurationB2() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationB2.class, PROPERTIES_ALLOW_NULL); + + assertExampleConfigurationsB2Equal( + serializer.deserialize(EXAMPLE_CONFIGURATION_B2_1), + ExampleInitializer.newExampleConfigurationB2_1() + ); + + assertExampleConfigurationsB2Equal( + serializer.deserialize(EXAMPLE_CONFIGURATION_B2_2), + ExampleInitializer.newExampleConfigurationB2_2() + ); + } + + @Test + void serializeExampleConfigurationA1() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationA1.class, PROPERTIES_ALLOW_NULL); + + ExampleConfigurationA1 config = ExampleInitializer.newExampleConfigurationA2(); + Map serialized = serializer.serialize(config); + assertEquals(EXAMPLE_CONFIGURATION_A1, serialized); + } + + @Test + void deserializeExampleConfigurationA1() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationA1.class, PROPERTIES_ALLOW_NULL); + + assertExampleConfigurationsA1Equal( + serializer.deserialize(EXAMPLE_CONFIGURATION_A1), + ExampleInitializer.newExampleConfigurationA2() + ); + + assertExampleConfigurationsA1Equal( + serializer.deserialize(EXAMPLE_CONFIGURATION_A2), + ExampleInitializer.newExampleConfigurationA2() + ); + } + + @Test + void serializeExampleConfigurationA2() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationA2.class, PROPERTIES_ALLOW_NULL); + + ExampleConfigurationA2 config = ExampleInitializer.newExampleConfigurationA2(); + Map serialized = serializer.serialize(config); + assertEquals(EXAMPLE_CONFIGURATION_A2, serialized); + } + + @Test + void deserializeExampleConfigurationA2() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationA2.class, PROPERTIES_ALLOW_NULL); + + assertExampleConfigurationsA2Equal( + serializer.deserialize(EXAMPLE_CONFIGURATION_A2), + ExampleInitializer.newExampleConfigurationA2() + ); + } + + @Test + void serializeExampleConfigurationsNullsWithNullCollectionElements() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationNulls.class, PROPERTIES_ALLOW_NULL); + + ExampleConfigurationNulls config1 = ExampleInitializer + .newExampleConfigurationNullsWithNullCollectionElements1(); + Map serialized1 = serializer.serialize(config1); + assertEquals(EXAMPLE_CONFIGURATION_NULLS_WITH_1, serialized1); + + ExampleConfigurationNulls config2 = ExampleInitializer + .newExampleConfigurationNullsWithNullCollectionElements2(); + Map serialized2 = serializer.serialize(config2); + assertEquals(EXAMPLE_CONFIGURATION_NULLS_WITH_2, serialized2); + } + + @Test + void deserializeExampleConfigurationNullsWithNullCollectionElements() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationNulls.class, PROPERTIES_ALLOW_NULL); + + assertExampleConfigurationsNullsEqual( + serializer.deserialize(EXAMPLE_CONFIGURATION_NULLS_WITH_1), + ExampleInitializer.newExampleConfigurationNullsWithNullCollectionElements1() + ); + + assertExampleConfigurationsNullsEqual( + serializer.deserialize(EXAMPLE_CONFIGURATION_NULLS_WITH_2), + ExampleInitializer.newExampleConfigurationNullsWithNullCollectionElements2() + ); + } + + @Test + void serializeExampleConfigurationsNullsWithoutNullCollectionElements() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationNulls.class, PROPERTIES_DENY_NULL); + + ExampleConfigurationNulls config1 = ExampleInitializer + .newExampleConfigurationNullsWithoutNullCollectionElements1(); + Map serialized1 = serializer.serialize(config1); + assertEquals(EXAMPLE_CONFIGURATION_NULLS_WITHOUT_1, serialized1); + + ExampleConfigurationNulls config2 = ExampleInitializer + .newExampleConfigurationNullsWithoutNullCollectionElements2(); + Map serialized2 = serializer.serialize(config2); + assertEquals(EXAMPLE_CONFIGURATION_NULLS_WITHOUT_2, serialized2); + } + + @Test + void deserializeExampleConfigurationNullsWithoutNullCollectionElements() { + ConfigurationSerializer serializer = + new ConfigurationSerializer<>(ExampleConfigurationNulls.class, PROPERTIES_DENY_NULL); + + assertExampleConfigurationsNullsEqual( + ExampleInitializer.newExampleConfigurationNullsWithoutNullCollectionElements1(), + serializer.deserialize(EXAMPLE_CONFIGURATION_NULLS_WITHOUT_1) + ); + + assertExampleConfigurationsNullsEqual( + ExampleInitializer.newExampleConfigurationNullsWithoutNullCollectionElements2(), + serializer.deserialize(EXAMPLE_CONFIGURATION_NULLS_WITHOUT_2) + ); + } + + private static void assertExampleConfigurationsNullsEqual( + ExampleConfigurationNulls expected, + ExampleConfigurationNulls actual + ) { + assertEquals(expected.getNullInteger(), actual.getNullInteger()); + assertEquals(expected.getNullString(), actual.getNullString()); + assertEquals(expected.getNullEnm(), actual.getNullEnm()); + assertEquals(expected.getNullB1(), actual.getNullB1()); + assertEquals(expected.getNullList(), actual.getNullList()); + assertEquals(expected.getNullArray(), actual.getNullArray()); + assertEquals(expected.getNullSet(), actual.getNullSet()); + assertEquals(expected.getNullMap(), actual.getNullMap()); + assertEquals(expected.getNullPoint(), actual.getNullPoint()); + assertEquals(expected.getListNullString(), actual.getListNullString()); + assertArrayEquals(expected.getArrayNullDouble(), actual.getArrayNullDouble()); + assertEquals(expected.getSetNullInteger(), actual.getSetNullInteger()); + assertEquals(expected.getMapNullEnmKey(), actual.getMapNullEnmKey()); + assertEquals(expected.getMapNullBigIntegerValue(), actual.getMapNullBigIntegerValue()); + assertEquals(expected, actual); + } + + private static void assertExampleConfigurationsA1Equal( + ExampleConfigurationA1 a1_1, + ExampleConfigurationA1 a1_2 + ) { + assertThat(ExampleConfigurationA1.getA1_staticFinalInt(), is(1)); + assertThat(ExampleConfigurationA1.getA1_staticInt(), is(2)); + + assertThat(a1_1.getA1_finalInt(), is(a1_2.getA1_finalInt())); + assertThat(a1_1.getA1_transientInt(), is(a1_2.getA1_transientInt())); + assertThat(a1_1.getA1_ignoredInt(), is(a1_2.getA1_ignoredInt())); + assertThat(a1_1.getA1_ignoredString(), is(a1_2.getA1_ignoredString())); + assertThat(a1_1.getA1_ignoredListString(), is(a1_2.getA1_ignoredListString())); + assertThat(a1_1.isA1_primBool(), is(a1_2.isA1_primBool())); + assertThat(a1_1.getA1_primChar(), is(a1_2.getA1_primChar())); + assertThat(a1_1.getA1_primByte(), is(a1_2.getA1_primByte())); + assertThat(a1_1.getA1_primShort(), is(a1_2.getA1_primShort())); + assertThat(a1_1.getA1_primInt(), is(a1_2.getA1_primInt())); + assertThat(a1_1.getA1_primLong(), is(a1_2.getA1_primLong())); + assertThat(a1_1.getA1_primFloat(), is(a1_2.getA1_primFloat())); + assertThat(a1_1.getA1_primDouble(), is(a1_2.getA1_primDouble())); + assertThat(a1_1.getA1_refBool(), is(a1_2.getA1_refBool())); + assertThat(a1_1.getA1_refChar(), is(a1_2.getA1_refChar())); + assertThat(a1_1.getA1_refByte(), is(a1_2.getA1_refByte())); + assertThat(a1_1.getA1_refShort(), is(a1_2.getA1_refShort())); + assertThat(a1_1.getA1_refInt(), is(a1_2.getA1_refInt())); + assertThat(a1_1.getA1_refLong(), is(a1_2.getA1_refLong())); + assertThat(a1_1.getA1_refFloat(), is(a1_2.getA1_refFloat())); + assertThat(a1_1.getA1_refDouble(), is(a1_2.getA1_refDouble())); + assertThat(a1_1.getA1_string(), is(a1_2.getA1_string())); + assertThat(a1_1.getA1_bigInteger(), is(a1_2.getA1_bigInteger())); + assertThat(a1_1.getA1_bigDecimal(), is(a1_2.getA1_bigDecimal())); + assertThat(a1_1.getA1_localDate(), is(a1_2.getA1_localDate())); + assertThat(a1_1.getA1_localTime(), is(a1_2.getA1_localTime())); + assertThat(a1_1.getA1_localDateTime(), is(a1_2.getA1_localDateTime())); + assertThat(a1_1.getA1_Enm(), is(a1_2.getA1_Enm())); + assertThat(a1_1.getA1_b1(), is(a1_2.getA1_b1())); + assertThat(a1_1.getA1_b2(), is(a1_2.getA1_b2())); + assertThat(a1_1.getA1_listBoolean(), is(a1_2.getA1_listBoolean())); + assertThat(a1_1.getA1_listChar(), is(a1_2.getA1_listChar())); + assertThat(a1_1.getA1_listByte(), is(a1_2.getA1_listByte())); + assertThat(a1_1.getA1_listShort(), is(a1_2.getA1_listShort())); + assertThat(a1_1.getA1_listInteger(), is(a1_2.getA1_listInteger())); + assertThat(a1_1.getA1_listLong(), is(a1_2.getA1_listLong())); + assertThat(a1_1.getA1_listFloat(), is(a1_2.getA1_listFloat())); + assertThat(a1_1.getA1_listDouble(), is(a1_2.getA1_listDouble())); + assertThat(a1_1.getA1_listString(), is(a1_2.getA1_listString())); + assertThat(a1_1.getA1_listBigInteger(), is(a1_2.getA1_listBigInteger())); + assertThat(a1_1.getA1_listBigDecimal(), is(a1_2.getA1_listBigDecimal())); + assertThat(a1_1.getA1_listLocalDate(), is(a1_2.getA1_listLocalDate())); + assertThat(a1_1.getA1_listLocalTime(), is(a1_2.getA1_listLocalTime())); + assertThat(a1_1.getA1_listLocalDateTime(), is(a1_2.getA1_listLocalDateTime())); + assertThat(a1_1.getA1_listEnm(), is(a1_2.getA1_listEnm())); + assertThat(a1_1.getA1_listB1(), is(a1_2.getA1_listB1())); + assertThat(a1_1.getA1_listB2(), is(a1_2.getA1_listB2())); + assertThat(a1_1.getA1_arrayPrimBoolean(), is(a1_2.getA1_arrayPrimBoolean())); + assertThat(a1_1.getA1_arrayPrimChar(), is(a1_2.getA1_arrayPrimChar())); + assertThat(a1_1.getA1_arrayPrimByte(), is(a1_2.getA1_arrayPrimByte())); + assertThat(a1_1.getA1_arrayPrimShort(), is(a1_2.getA1_arrayPrimShort())); + assertThat(a1_1.getA1_arrayPrimInteger(), is(a1_2.getA1_arrayPrimInteger())); + assertThat(a1_1.getA1_arrayPrimLong(), is(a1_2.getA1_arrayPrimLong())); + assertThat(a1_1.getA1_arrayPrimFloat(), is(a1_2.getA1_arrayPrimFloat())); + assertThat(a1_1.getA1_arrayPrimDouble(), is(a1_2.getA1_arrayPrimDouble())); + assertThat(a1_1.getA1_arrayBoolean(), is(a1_2.getA1_arrayBoolean())); + assertThat(a1_1.getA1_arrayChar(), is(a1_2.getA1_arrayChar())); + assertThat(a1_1.getA1_arrayByte(), is(a1_2.getA1_arrayByte())); + assertThat(a1_1.getA1_arrayShort(), is(a1_2.getA1_arrayShort())); + assertThat(a1_1.getA1_arrayInteger(), is(a1_2.getA1_arrayInteger())); + assertThat(a1_1.getA1_arrayLong(), is(a1_2.getA1_arrayLong())); + assertThat(a1_1.getA1_arrayFloat(), is(a1_2.getA1_arrayFloat())); + assertThat(a1_1.getA1_arrayDouble(), is(a1_2.getA1_arrayDouble())); + assertThat(a1_1.getA1_arrayString(), is(a1_2.getA1_arrayString())); + assertThat(a1_1.getA1_arrayBigInteger(), is(a1_2.getA1_arrayBigInteger())); + assertThat(a1_1.getA1_arrayBigDecimal(), is(a1_2.getA1_arrayBigDecimal())); + assertThat(a1_1.getA1_arrayLocalDate(), is(a1_2.getA1_arrayLocalDate())); + assertThat(a1_1.getA1_arrayLocalTime(), is(a1_2.getA1_arrayLocalTime())); + assertThat(a1_1.getA1_arrayLocalDateTime(), is(a1_2.getA1_arrayLocalDateTime())); + assertThat(a1_1.getA1_arrayEnm(), is(a1_2.getA1_arrayEnm())); + assertThat(a1_1.getA1_arrayB1(), is(a1_2.getA1_arrayB1())); + assertThat(a1_1.getA1_arrayB2(), is(a1_2.getA1_arrayB2())); + assertThat(a1_1.getA1_setBoolean(), is(a1_2.getA1_setBoolean())); + assertThat(a1_1.getA1_setChar(), is(a1_2.getA1_setChar())); + assertThat(a1_1.getA1_setByte(), is(a1_2.getA1_setByte())); + assertThat(a1_1.getA1_setShort(), is(a1_2.getA1_setShort())); + assertThat(a1_1.getA1_setInteger(), is(a1_2.getA1_setInteger())); + assertThat(a1_1.getA1_setLong(), is(a1_2.getA1_setLong())); + assertThat(a1_1.getA1_setFloat(), is(a1_2.getA1_setFloat())); + assertThat(a1_1.getA1_setDouble(), is(a1_2.getA1_setDouble())); + assertThat(a1_1.getA1_setString(), is(a1_2.getA1_setString())); + assertThat(a1_1.getA1_setBigInteger(), is(a1_2.getA1_setBigInteger())); + assertThat(a1_1.getA1_setBigDecimal(), is(a1_2.getA1_setBigDecimal())); + assertThat(a1_1.getA1_setLocalDate(), is(a1_2.getA1_setLocalDate())); + assertThat(a1_1.getA1_setLocalTime(), is(a1_2.getA1_setLocalTime())); + assertThat(a1_1.getA1_setLocalDateTime(), is(a1_2.getA1_setLocalDateTime())); + assertThat(a1_1.getA1_setEnm(), is(a1_2.getA1_setEnm())); + assertThat(a1_1.getA1_setB1(), is(a1_2.getA1_setB1())); + assertThat(a1_1.getA1_setB2(), is(a1_2.getA1_setB2())); + assertThat(a1_1.getA1_mapBooleanBoolean(), is(a1_2.getA1_mapBooleanBoolean())); + assertThat(a1_1.getA1_mapCharChar(), is(a1_2.getA1_mapCharChar())); + assertThat(a1_1.getA1_mapByteByte(), is(a1_2.getA1_mapByteByte())); + assertThat(a1_1.getA1_mapShortShort(), is(a1_2.getA1_mapShortShort())); + assertThat(a1_1.getA1_mapIntegerInteger(), is(a1_2.getA1_mapIntegerInteger())); + assertThat(a1_1.getA1_mapLongLong(), is(a1_2.getA1_mapLongLong())); + assertThat(a1_1.getA1_mapFloatFloat(), is(a1_2.getA1_mapFloatFloat())); + assertThat(a1_1.getA1_mapDoubleDouble(), is(a1_2.getA1_mapDoubleDouble())); + assertThat(a1_1.getA1_mapStringString(), is(a1_2.getA1_mapStringString())); + assertThat(a1_1.getA1_mapBigIntegerBigInteger(), is(a1_2.getA1_mapBigIntegerBigInteger())); + assertThat(a1_1.getA1_mapBigDecimalBigDecimal(), is(a1_2.getA1_mapBigDecimalBigDecimal())); + assertThat(a1_1.getA1_mapLocalDateLocalDate(), is(a1_2.getA1_mapLocalDateLocalDate())); + assertThat(a1_1.getA1_mapLocalTimeLocalTime(), is(a1_2.getA1_mapLocalTimeLocalTime())); + assertThat(a1_1.getA1_mapLocalDateTimeLocalDateTime(), is(a1_2.getA1_mapLocalDateTimeLocalDateTime())); + assertThat(a1_1.getA1_mapEnmEnm(), is(a1_2.getA1_mapEnmEnm())); + assertThat(a1_1.getA1_mapIntegerB1(), is(a1_2.getA1_mapIntegerB1())); + assertThat(a1_1.getA1_mapEnmB2(), is(a1_2.getA1_mapEnmB2())); + assertThat(a1_1.getA1_listEmpty(), is(a1_2.getA1_listEmpty())); + assertThat(a1_1.getA1_arrayEmpty(), is(a1_2.getA1_arrayEmpty())); + assertThat(a1_1.getA1_setEmpty(), is(a1_2.getA1_setEmpty())); + assertThat(a1_1.getA1_mapEmpty(), is(a1_2.getA1_mapEmpty())); + assertThat(a1_1.getA1_listListByte(), is(a1_2.getA1_listListByte())); + assertDeepEquals(a1_1.getA1_listArrayFloat(), a1_2.getA1_listArrayFloat(), ArrayList::new); + assertThat(a1_1.getA1_listSetString(), is(a1_2.getA1_listSetString())); + assertThat(a1_1.getA1_listMapEnmLocalDate(), is(a1_2.getA1_listMapEnmLocalDate())); + assertThat(a1_1.getA1_setSetShort(), is(a1_2.getA1_setSetShort())); + assertDeepEquals(a1_1.getA1_setArrayDouble(), a1_2.getA1_setArrayDouble(), HashSet::new); + assertThat(a1_1.getA1_setListString(), is(a1_2.getA1_setListString())); + assertThat(a1_1.getA1_setMapEnmLocalTime(), is(a1_2.getA1_setMapEnmLocalTime())); + assertThat(a1_1.getA1_mapIntegerMapLongBoolean(), is(a1_2.getA1_mapIntegerMapLongBoolean())); + assertThat(a1_1.getA1_mapStringListB1(), is(a1_2.getA1_mapStringListB1())); + assertEquals(a1_2.getA1_mapBigIntegerArrayBigDecimal().keySet(), a1_1.getA1_mapBigIntegerArrayBigDecimal().keySet()); + assertDeepEquals(a1_2.getA1_mapBigIntegerArrayBigDecimal().values(), a1_2.getA1_mapBigIntegerArrayBigDecimal().values(), HashSet::new); + assertThat(a1_1.getA1_mapEnmSetB2(), is(a1_2.getA1_mapEnmSetB2())); + assertThat(a1_1.getA1_mapIntegerListMapShortSetB2(), is(a1_2.getA1_mapIntegerListMapShortSetB2())); + assertThat(a1_1.getA1_arrayArrayPrimBoolean(), is(a1_2.getA1_arrayArrayPrimBoolean())); + assertThat(a1_1.getA1_arrayArrayPrimChar(), is(a1_2.getA1_arrayArrayPrimChar())); + assertThat(a1_1.getA1_arrayArrayPrimByte(), is(a1_2.getA1_arrayArrayPrimByte())); + assertThat(a1_1.getA1_arrayArrayPrimShort(), is(a1_2.getA1_arrayArrayPrimShort())); + assertThat(a1_1.getA1_arrayArrayPrimInteger(), is(a1_2.getA1_arrayArrayPrimInteger())); + assertThat(a1_1.getA1_arrayArrayPrimLong(), is(a1_2.getA1_arrayArrayPrimLong())); + assertThat(a1_1.getA1_arrayArrayPrimFloat(), is(a1_2.getA1_arrayArrayPrimFloat())); + assertThat(a1_1.getA1_arrayArrayPrimDouble(), is(a1_2.getA1_arrayArrayPrimDouble())); + assertThat(a1_1.getA1_arrayArrayBoolean(), is(a1_2.getA1_arrayArrayBoolean())); + assertThat(a1_1.getA1_arrayArrayChar(), is(a1_2.getA1_arrayArrayChar())); + assertThat(a1_1.getA1_arrayArrayByte(), is(a1_2.getA1_arrayArrayByte())); + assertThat(a1_1.getA1_arrayArrayShort(), is(a1_2.getA1_arrayArrayShort())); + assertThat(a1_1.getA1_arrayArrayInteger(), is(a1_2.getA1_arrayArrayInteger())); + assertThat(a1_1.getA1_arrayArrayLong(), is(a1_2.getA1_arrayArrayLong())); + assertThat(a1_1.getA1_arrayArrayFloat(), is(a1_2.getA1_arrayArrayFloat())); + assertThat(a1_1.getA1_arrayArrayDouble(), is(a1_2.getA1_arrayArrayDouble())); + assertThat(a1_1.getA1_arrayArrayString(), is(a1_2.getA1_arrayArrayString())); + assertThat(a1_1.getA1_arrayArrayBigInteger(), is(a1_2.getA1_arrayArrayBigInteger())); + assertThat(a1_1.getA1_arrayArrayBigDecimal(), is(a1_2.getA1_arrayArrayBigDecimal())); + assertThat(a1_1.getA1_arrayArrayLocalDate(), is(a1_2.getA1_arrayArrayLocalDate())); + assertThat(a1_1.getA1_arrayArrayLocalTime(), is(a1_2.getA1_arrayArrayLocalTime())); + assertThat(a1_1.getA1_arrayArrayLocalDateTime(), is(a1_2.getA1_arrayArrayLocalDateTime())); + assertThat(a1_1.getA1_arrayArrayEnm(), is(a1_2.getA1_arrayArrayEnm())); + assertThat(a1_1.getA1_arrayArrayB1(), is(a1_2.getA1_arrayArrayB1())); + assertThat(a1_1.getA1_arrayArrayB2(), is(a1_2.getA1_arrayArrayB2())); + assertThat(a1_1.getA1_point(), is(a1_2.getA1_point())); + assertThat(a1_1.getA1_listPoint(), is(a1_2.getA1_listPoint())); + assertThat(a1_1.getA1_arrayPoint(), is(a1_2.getA1_arrayPoint())); + assertThat(a1_1.getA1_setPoint(), is(a1_2.getA1_setPoint())); + assertThat(a1_1.getA1_mapEnmListPoint(), is(a1_2.getA1_mapEnmListPoint())); + } + + static void assertExampleConfigurationsA2Equal( + ExampleConfigurationA2 a2_1, + ExampleConfigurationA2 a2_2 + ) { + assertThat(ExampleConfigurationA2.getA1_staticFinalInt(), is(1)); + assertThat(ExampleConfigurationA2.getA1_staticInt(), is(2)); + + assertExampleConfigurationsA1Equal(a2_1, a2_2); + + assertThat(a2_1.getA2_finalInt(), is(a2_2.getA2_finalInt())); + assertThat(a2_1.getA2_transientInt(), is(a2_2.getA2_transientInt())); + assertThat(a2_1.getA2_ignoredInt(), is(a2_2.getA2_ignoredInt())); + assertThat(a2_1.getA2_ignoredString(), is(a2_2.getA2_ignoredString())); + assertThat(a2_1.getA2_ignoredListString(), is(a2_2.getA2_ignoredListString())); + assertThat(a2_1.isA2_primBool(), is(a2_2.isA2_primBool())); + assertThat(a2_1.getA2_primChar(), is(a2_2.getA2_primChar())); + assertThat(a2_1.getA2_primByte(), is(a2_2.getA2_primByte())); + assertThat(a2_1.getA2_primShort(), is(a2_2.getA2_primShort())); + assertThat(a2_1.getA2_primInt(), is(a2_2.getA2_primInt())); + assertThat(a2_1.getA2_primLong(), is(a2_2.getA2_primLong())); + assertThat(a2_1.getA2_primFloat(), is(a2_2.getA2_primFloat())); + assertThat(a2_1.getA2_primDouble(), is(a2_2.getA2_primDouble())); + assertThat(a2_1.getA2_refBool(), is(a2_2.getA2_refBool())); + assertThat(a2_1.getA2_refChar(), is(a2_2.getA2_refChar())); + assertThat(a2_1.getA2_refByte(), is(a2_2.getA2_refByte())); + assertThat(a2_1.getA2_refShort(), is(a2_2.getA2_refShort())); + assertThat(a2_1.getA2_refInt(), is(a2_2.getA2_refInt())); + assertThat(a2_1.getA2_refLong(), is(a2_2.getA2_refLong())); + assertThat(a2_1.getA2_refFloat(), is(a2_2.getA2_refFloat())); + assertThat(a2_1.getA2_refDouble(), is(a2_2.getA2_refDouble())); + assertThat(a2_1.getA2_string(), is(a2_2.getA2_string())); + assertThat(a2_1.getA2_bigInteger(), is(a2_2.getA2_bigInteger())); + assertThat(a2_1.getA2_bigDecimal(), is(a2_2.getA2_bigDecimal())); + assertThat(a2_1.getA2_localDate(), is(a2_2.getA2_localDate())); + assertThat(a2_1.getA2_localTime(), is(a2_2.getA2_localTime())); + assertThat(a2_1.getA2_localDateTime(), is(a2_2.getA2_localDateTime())); + assertThat(a2_1.getA2_Enm(), is(a2_2.getA2_Enm())); + assertThat(a2_1.getA2_b1(), is(a2_2.getA2_b1())); + assertThat(a2_1.getA2_b2(), is(a2_2.getA2_b2())); + assertThat(a2_1.getA2_listBoolean(), is(a2_2.getA2_listBoolean())); + assertThat(a2_1.getA2_listChar(), is(a2_2.getA2_listChar())); + assertThat(a2_1.getA2_listByte(), is(a2_2.getA2_listByte())); + assertThat(a2_1.getA2_listShort(), is(a2_2.getA2_listShort())); + assertThat(a2_1.getA2_listInteger(), is(a2_2.getA2_listInteger())); + assertThat(a2_1.getA2_listLong(), is(a2_2.getA2_listLong())); + assertThat(a2_1.getA2_listFloat(), is(a2_2.getA2_listFloat())); + assertThat(a2_1.getA2_listDouble(), is(a2_2.getA2_listDouble())); + assertThat(a2_1.getA2_listString(), is(a2_2.getA2_listString())); + assertThat(a2_1.getA2_listBigInteger(), is(a2_2.getA2_listBigInteger())); + assertThat(a2_1.getA2_listBigDecimal(), is(a2_2.getA2_listBigDecimal())); + assertThat(a2_1.getA2_listLocalDate(), is(a2_2.getA2_listLocalDate())); + assertThat(a2_1.getA2_listLocalTime(), is(a2_2.getA2_listLocalTime())); + assertThat(a2_1.getA2_listLocalDateTime(), is(a2_2.getA2_listLocalDateTime())); + assertThat(a2_1.getA2_listEnm(), is(a2_2.getA2_listEnm())); + assertThat(a2_1.getA2_listB1(), is(a2_2.getA2_listB1())); + assertThat(a2_1.getA2_listB2(), is(a2_2.getA2_listB2())); + assertThat(a2_1.getA2_arrayPrimBoolean(), is(a2_2.getA2_arrayPrimBoolean())); + assertThat(a2_1.getA2_arrayPrimChar(), is(a2_2.getA2_arrayPrimChar())); + assertThat(a2_1.getA2_arrayPrimByte(), is(a2_2.getA2_arrayPrimByte())); + assertThat(a2_1.getA2_arrayPrimShort(), is(a2_2.getA2_arrayPrimShort())); + assertThat(a2_1.getA2_arrayPrimInteger(), is(a2_2.getA2_arrayPrimInteger())); + assertThat(a2_1.getA2_arrayPrimLong(), is(a2_2.getA2_arrayPrimLong())); + assertThat(a2_1.getA2_arrayPrimFloat(), is(a2_2.getA2_arrayPrimFloat())); + assertThat(a2_1.getA2_arrayPrimDouble(), is(a2_2.getA2_arrayPrimDouble())); + assertThat(a2_1.getA2_arrayBoolean(), is(a2_2.getA2_arrayBoolean())); + assertThat(a2_1.getA2_arrayChar(), is(a2_2.getA2_arrayChar())); + assertThat(a2_1.getA2_arrayByte(), is(a2_2.getA2_arrayByte())); + assertThat(a2_1.getA2_arrayShort(), is(a2_2.getA2_arrayShort())); + assertThat(a2_1.getA2_arrayInteger(), is(a2_2.getA2_arrayInteger())); + assertThat(a2_1.getA2_arrayLong(), is(a2_2.getA2_arrayLong())); + assertThat(a2_1.getA2_arrayFloat(), is(a2_2.getA2_arrayFloat())); + assertThat(a2_1.getA2_arrayDouble(), is(a2_2.getA2_arrayDouble())); + assertThat(a2_1.getA2_arrayString(), is(a2_2.getA2_arrayString())); + assertThat(a2_1.getA2_arrayBigInteger(), is(a2_2.getA2_arrayBigInteger())); + assertThat(a2_1.getA2_arrayBigDecimal(), is(a2_2.getA2_arrayBigDecimal())); + assertThat(a2_1.getA2_arrayLocalDate(), is(a2_2.getA2_arrayLocalDate())); + assertThat(a2_1.getA2_arrayLocalTime(), is(a2_2.getA2_arrayLocalTime())); + assertThat(a2_1.getA2_arrayLocalDateTime(), is(a2_2.getA2_arrayLocalDateTime())); + assertThat(a2_1.getA2_arrayEnm(), is(a2_2.getA2_arrayEnm())); + assertThat(a2_1.getA2_arrayB1(), is(a2_2.getA2_arrayB1())); + assertThat(a2_1.getA2_arrayB2(), is(a2_2.getA2_arrayB2())); + assertThat(a2_1.getA2_setBoolean(), is(a2_2.getA2_setBoolean())); + assertThat(a2_1.getA2_setChar(), is(a2_2.getA2_setChar())); + assertThat(a2_1.getA2_setByte(), is(a2_2.getA2_setByte())); + assertThat(a2_1.getA2_setShort(), is(a2_2.getA2_setShort())); + assertThat(a2_1.getA2_setInteger(), is(a2_2.getA2_setInteger())); + assertThat(a2_1.getA2_setLong(), is(a2_2.getA2_setLong())); + assertThat(a2_1.getA2_setFloat(), is(a2_2.getA2_setFloat())); + assertThat(a2_1.getA2_setDouble(), is(a2_2.getA2_setDouble())); + assertThat(a2_1.getA2_setString(), is(a2_2.getA2_setString())); + assertThat(a2_1.getA2_setBigInteger(), is(a2_2.getA2_setBigInteger())); + assertThat(a2_1.getA2_setBigDecimal(), is(a2_2.getA2_setBigDecimal())); + assertThat(a2_1.getA2_setLocalDate(), is(a2_2.getA2_setLocalDate())); + assertThat(a2_1.getA2_setLocalTime(), is(a2_2.getA2_setLocalTime())); + assertThat(a2_1.getA2_setLocalDateTime(), is(a2_2.getA2_setLocalDateTime())); + assertThat(a2_1.getA2_setEnm(), is(a2_2.getA2_setEnm())); + assertThat(a2_1.getA2_setB1(), is(a2_2.getA2_setB1())); + assertThat(a2_1.getA2_setB2(), is(a2_2.getA2_setB2())); + assertThat(a2_1.getA2_mapBooleanBoolean(), is(a2_2.getA2_mapBooleanBoolean())); + assertThat(a2_1.getA2_mapCharChar(), is(a2_2.getA2_mapCharChar())); + assertThat(a2_1.getA2_mapByteByte(), is(a2_2.getA2_mapByteByte())); + assertThat(a2_1.getA2_mapShortShort(), is(a2_2.getA2_mapShortShort())); + assertThat(a2_1.getA2_mapIntegerInteger(), is(a2_2.getA2_mapIntegerInteger())); + assertThat(a2_1.getA2_mapLongLong(), is(a2_2.getA2_mapLongLong())); + assertThat(a2_1.getA2_mapFloatFloat(), is(a2_2.getA2_mapFloatFloat())); + assertThat(a2_1.getA2_mapDoubleDouble(), is(a2_2.getA2_mapDoubleDouble())); + assertThat(a2_1.getA2_mapStringString(), is(a2_2.getA2_mapStringString())); + assertThat(a2_1.getA2_mapBigIntegerBigInteger(), is(a2_2.getA2_mapBigIntegerBigInteger())); + assertThat(a2_1.getA2_mapBigDecimalBigDecimal(), is(a2_2.getA2_mapBigDecimalBigDecimal())); + assertThat(a2_1.getA2_mapLocalDateLocalDate(), is(a2_2.getA2_mapLocalDateLocalDate())); + assertThat(a2_1.getA2_mapLocalTimeLocalTime(), is(a2_2.getA2_mapLocalTimeLocalTime())); + assertThat(a2_1.getA2_mapLocalDateTimeLocalDateTime(), is(a2_2.getA2_mapLocalDateTimeLocalDateTime())); + assertThat(a2_1.getA2_mapEnmEnm(), is(a2_2.getA2_mapEnmEnm())); + assertThat(a2_1.getA2_mapIntegerB1(), is(a2_2.getA2_mapIntegerB1())); + assertThat(a2_1.getA2_mapEnmB2(), is(a2_2.getA2_mapEnmB2())); + assertThat(a2_1.getA2_listEmpty(), is(a2_2.getA2_listEmpty())); + assertThat(a2_1.getA2_arrayEmpty(), is(a2_2.getA2_arrayEmpty())); + assertThat(a2_1.getA2_setEmpty(), is(a2_2.getA2_setEmpty())); + assertThat(a2_1.getA2_mapEmpty(), is(a2_2.getA2_mapEmpty())); + assertThat(a2_1.getA2_listListByte(), is(a2_2.getA2_listListByte())); + assertDeepEquals(a2_1.getA2_listArrayFloat(), a2_2.getA2_listArrayFloat(), ArrayList::new); + assertThat(a2_1.getA2_listSetString(), is(a2_2.getA2_listSetString())); + assertThat(a2_1.getA2_listMapEnmLocalDate(), is(a2_2.getA2_listMapEnmLocalDate())); + assertThat(a2_1.getA2_setSetShort(), is(a2_2.getA2_setSetShort())); + assertDeepEquals(a2_1.getA2_setArrayDouble(), a2_2.getA2_setArrayDouble(), HashSet::new); + assertThat(a2_1.getA2_setListString(), is(a2_2.getA2_setListString())); + assertThat(a2_1.getA2_setMapEnmLocalTime(), is(a2_2.getA2_setMapEnmLocalTime())); + assertThat(a2_1.getA2_mapIntegerMapLongBoolean(), is(a2_2.getA2_mapIntegerMapLongBoolean())); + assertThat(a2_1.getA2_mapStringListB1(), is(a2_2.getA2_mapStringListB1())); + assertEquals(a2_2.getA2_mapBigIntegerArrayBigDecimal().keySet(), a2_1.getA2_mapBigIntegerArrayBigDecimal().keySet()); + assertDeepEquals(a2_2.getA2_mapBigIntegerArrayBigDecimal().values(), a2_2.getA2_mapBigIntegerArrayBigDecimal().values(), HashSet::new); + assertThat(a2_1.getA2_mapEnmSetB2(), is(a2_2.getA2_mapEnmSetB2())); + assertThat(a2_1.getA2_mapIntegerListMapShortSetB2(), is(a2_2.getA2_mapIntegerListMapShortSetB2())); + assertThat(a2_1.getA2_arrayArrayPrimBoolean(), is(a2_2.getA2_arrayArrayPrimBoolean())); + assertThat(a2_1.getA2_arrayArrayPrimChar(), is(a2_2.getA2_arrayArrayPrimChar())); + assertThat(a2_1.getA2_arrayArrayPrimByte(), is(a2_2.getA2_arrayArrayPrimByte())); + assertThat(a2_1.getA2_arrayArrayPrimShort(), is(a2_2.getA2_arrayArrayPrimShort())); + assertThat(a2_1.getA2_arrayArrayPrimInteger(), is(a2_2.getA2_arrayArrayPrimInteger())); + assertThat(a2_1.getA2_arrayArrayPrimLong(), is(a2_2.getA2_arrayArrayPrimLong())); + assertThat(a2_1.getA2_arrayArrayPrimFloat(), is(a2_2.getA2_arrayArrayPrimFloat())); + assertThat(a2_1.getA2_arrayArrayPrimDouble(), is(a2_2.getA2_arrayArrayPrimDouble())); + assertThat(a2_1.getA2_arrayArrayBoolean(), is(a2_2.getA2_arrayArrayBoolean())); + assertThat(a2_1.getA2_arrayArrayChar(), is(a2_2.getA2_arrayArrayChar())); + assertThat(a2_1.getA2_arrayArrayByte(), is(a2_2.getA2_arrayArrayByte())); + assertThat(a2_1.getA2_arrayArrayShort(), is(a2_2.getA2_arrayArrayShort())); + assertThat(a2_1.getA2_arrayArrayInteger(), is(a2_2.getA2_arrayArrayInteger())); + assertThat(a2_1.getA2_arrayArrayLong(), is(a2_2.getA2_arrayArrayLong())); + assertThat(a2_1.getA2_arrayArrayFloat(), is(a2_2.getA2_arrayArrayFloat())); + assertThat(a2_1.getA2_arrayArrayDouble(), is(a2_2.getA2_arrayArrayDouble())); + assertThat(a2_1.getA2_arrayArrayString(), is(a2_2.getA2_arrayArrayString())); + assertThat(a2_1.getA2_arrayArrayBigInteger(), is(a2_2.getA2_arrayArrayBigInteger())); + assertThat(a2_1.getA2_arrayArrayBigDecimal(), is(a2_2.getA2_arrayArrayBigDecimal())); + assertThat(a2_1.getA2_arrayArrayLocalDate(), is(a2_2.getA2_arrayArrayLocalDate())); + assertThat(a2_1.getA2_arrayArrayLocalTime(), is(a2_2.getA2_arrayArrayLocalTime())); + assertThat(a2_1.getA2_arrayArrayLocalDateTime(), is(a2_2.getA2_arrayArrayLocalDateTime())); + assertThat(a2_1.getA2_arrayArrayEnm(), is(a2_2.getA2_arrayArrayEnm())); + assertThat(a2_1.getA2_arrayArrayB1(), is(a2_2.getA2_arrayArrayB1())); + assertThat(a2_1.getA2_arrayArrayB2(), is(a2_2.getA2_arrayArrayB2())); + assertThat(a2_1.getA2_point(), is(a2_2.getA2_point())); + assertThat(a2_1.getA2_listPoint(), is(a2_2.getA2_listPoint())); + assertThat(a2_1.getA2_arrayPoint(), is(a2_2.getA2_arrayPoint())); + assertThat(a2_1.getA2_setPoint(), is(a2_2.getA2_setPoint())); + assertThat(a2_1.getA2_mapEnmListPoint(), is(a2_2.getA2_mapEnmListPoint())); + } + + + private static void assertExampleConfigurationsB1Equal( + ExampleConfigurationB1 b1_1, + ExampleConfigurationB1 b1_2 + ) { + assertThat(ExampleConfigurationB1.getB1_staticFinalInt(), is(1)); + assertThat(ExampleConfigurationB1.getB1_staticInt(), is(2)); + + assertThat(b1_1.getB1_finalInt(), is(b1_2.getB1_finalInt())); + assertThat(b1_1.getB1_transientInt(), is(b1_2.getB1_transientInt())); + assertThat(b1_1.getB1_ignoredInt(), is(b1_2.getB1_ignoredInt())); + assertThat(b1_1.getB1_ignoredString(), is(b1_2.getB1_ignoredString())); + assertThat(b1_1.getB1_ignoredListString(), is(b1_2.getB1_ignoredListString())); + assertThat(b1_1.isB1_primBool(), is(b1_2.isB1_primBool())); + assertThat(b1_1.getB1_refChar(), is(b1_2.getB1_refChar())); + assertThat(b1_1.getB1_string(), is(b1_2.getB1_string())); + assertThat(b1_1.getB1_listByte(), is(b1_2.getB1_listByte())); + assertThat(b1_1.getB1_arrayShort(), is(b1_2.getB1_arrayShort())); + assertThat(b1_1.getB1_setInteger(), is(b1_2.getB1_setInteger())); + assertThat(b1_1.getB1_listEmpty(), is(b1_2.getB1_listEmpty())); + assertThat(b1_1.getB1_mapLongLong(), is(b1_2.getB1_mapLongLong())); + assertThat(b1_1.getB1_listListByte(), is(b1_2.getB1_listListByte())); + assertThat(b1_1.getB1_point(), is(b1_2.getB1_point())); + assertEquals(b1_2, b1_1); + } + + private static void assertExampleConfigurationsB2Equal( + ExampleConfigurationB2 b2_1, + ExampleConfigurationB2 b2_2 + ) { + assertExampleConfigurationsB1Equal(b2_1, b2_2); + + assertThat(ExampleConfigurationB2.getB1_staticFinalInt(), is(1)); + assertThat(ExampleConfigurationB2.getB1_staticInt(), is(2)); + + assertThat(b2_1.getB2_finalInt(), is(b2_2.getB2_finalInt())); + assertThat(b2_1.getB2_transientInt(), is(b2_2.getB2_transientInt())); + assertThat(b2_1.getB2_ignoredInt(), is(b2_2.getB2_ignoredInt())); + assertThat(b2_1.getB2_ignoredString(), is(b2_2.getB2_ignoredString())); + assertThat(b2_1.getB2_ignoredListString(), is(b2_2.getB2_ignoredListString())); + assertThat(b2_1.getB2_primChar(), is(b2_2.getB2_primChar())); + assertThat(b2_1.getB2_refBool(), is(b2_2.getB2_refBool())); + assertThat(b2_1.getB2_bigInteger(), is(b2_2.getB2_bigInteger())); + assertThat(b2_1.getB2_listShort(), is(b2_2.getB2_listShort())); + assertThat(b2_1.getB2_arrayInteger(), is(b2_2.getB2_arrayInteger())); + assertThat(b2_1.getB2_setLong(), is(b2_2.getB2_setLong())); + assertThat(b2_1.getB2_arrayEmpty(), is(b2_2.getB2_arrayEmpty())); + assertThat(b2_1.getB2_mapFloatFloat(), is(b2_2.getB2_mapFloatFloat())); + assertDeepEquals(b2_1.getB2_setArrayDouble(), b2_2.getB2_setArrayDouble(), LinkedHashSet::new); + assertThat(b2_1.getB2_listPoint(), is(b2_2.getB2_listPoint())); + assertEquals(b2_2, b2_1); + } + + private static > void assertDeepEquals( + C collection1, + C collection2, + Supplier>> collectionFactory + ) { + Collection> c1 = collection1.stream().map(Arrays::asList) + .collect(Collectors.toCollection(collectionFactory)); + Collection> c2 = collection2.stream().map(Arrays::asList) + .collect(Collectors.toCollection(collectionFactory)); + assertEquals(c2, c1); + } + + @Test + void yamlStoreSavesAndLoadsExampleConfigurationA2() { + var properties = YamlConfigurationProperties.newBuilder() + .addSerializer(Point.class, TestUtils.POINT_SERIALIZER) + .build(); + var store = new YamlConfigurationStore<>(ExampleConfigurationA2.class, properties); + ExampleConfigurationA2 cfg1 = ExampleInitializer.newExampleConfigurationA2(); + store.save(cfg1, yamlFile); + ExampleConfigurationA2 cfg2 = store.load(yamlFile); + assertExampleConfigurationsA2Equal(cfg1, cfg2); + } + + @Test + void yamlStoreSavesAndLoadsExampleConfigurationNullsWithNullCollectionElements1() { + var properties = YamlConfigurationProperties.newBuilder() + .addSerializer(Point.class, TestUtils.POINT_SERIALIZER) + .outputNulls(true) + .inputNulls(true) + .build(); + var store = new YamlConfigurationStore<>(ExampleConfigurationNulls.class, properties); + ExampleConfigurationNulls cfg1 = ExampleInitializer + .newExampleConfigurationNullsWithNullCollectionElements1(); + store.save(cfg1, yamlFile); + ExampleConfigurationNulls cfg2 = store.load(yamlFile); + assertExampleConfigurationsNullsEqual(cfg1, cfg2); + } + + @Test + void yamlStoreSavesAndLoadsExampleConfigurationNullsWithoutNullCollectionElements1() { + var properties = YamlConfigurationProperties.newBuilder() + .addSerializer(Point.class, TestUtils.POINT_SERIALIZER) + .build(); + var store = new YamlConfigurationStore<>(ExampleConfigurationNulls.class, properties); + ExampleConfigurationNulls cfg1 = ExampleInitializer + .newExampleConfigurationNullsWithoutNullCollectionElements1(); + store.save(cfg1, yamlFile); + ExampleConfigurationNulls cfg2 = store.load(yamlFile); + assertExampleConfigurationsNullsEqual(cfg1, cfg2); + } +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/FieldExtractorTest.java b/configlib-core/src/test/java/de/exlll/configlib/FieldExtractorTest.java new file mode 100644 index 0000000..25e7d3c --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/FieldExtractorTest.java @@ -0,0 +1,16 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.*; + +class FieldExtractorTest { + @Test + void applyCallsExtract() { + FieldExtractor extractor = mock(FieldExtractor.class, CALLS_REAL_METHODS); + + extractor.apply(Object.class); + + verify(extractor).extract(Object.class); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/FieldExtractorsTest.java b/configlib-core/src/test/java/de/exlll/configlib/FieldExtractorsTest.java new file mode 100644 index 0000000..f068a9e --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/FieldExtractorsTest.java @@ -0,0 +1,102 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.List; + +import static de.exlll.configlib.TestUtils.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +class FieldExtractorsTest { + @Test + void extractConfigurationRequiresNonNull() { + assertThrowsNullPointerException( + () -> FieldExtractors.CONFIGURATION.extract(null), + "configuration class" + ); + } + + @Test + void extractConfigurationRequiresConfiguration() { + assertThrowsConfigurationException( + () -> FieldExtractors.CONFIGURATION.extract(getClass()), + "Class 'FieldExtractorsTest' must be a configuration." + ); + } + + @Test + void extractConfigurationDirectly() { + @Configuration + class A { + int i; + } + + FieldExtractor extractor = FieldExtractors.CONFIGURATION; + + List actual = extractor.extract(A.class).toList(); + List expected = List.of( + getField(A.class, "i") + ); + assertThat(actual, is(expected)); + } + + @Test + void extractConfigurationIndirectly() { + @Configuration + class A { + int i; + } + class B extends A { + int j; + } + class C extends B { + int k; + } + + FieldExtractor extractor = FieldExtractors.CONFIGURATION; + + List actual = extractor.extract(C.class).toList(); + List expected = List.of( + getField(A.class, "i"), + getField(B.class, "j"), + getField(C.class, "k") + ); + assertThat(actual, is(expected)); + } + + @Test + void extractConfigurationRequiresNoShadowing() { + @Configuration + class A { + int i; + } + class B extends A { + int i; + } + assertThrowsConfigurationException( + () -> FieldExtractors.CONFIGURATION.extract(B.class), + "Shadowing of fields is not supported. Field 'i' of class B " + + "shadows field 'i' of class A." + ); + } + + @Test + void extractConfigurationAppliesDefaultFieldFilter() { + class A { + @Configuration + class B { + private final int i = 1; + @Ignore + private int j; + private static int k; + private transient int l; + private int m; + } + } + List actual = FieldExtractors.CONFIGURATION.extract(A.B.class).toList(); + List expected = List.of(getField(A.B.class, "m")); + assertThat(actual, is(expected)); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/FieldFiltersTest.java b/configlib-core/src/test/java/de/exlll/configlib/FieldFiltersTest.java new file mode 100644 index 0000000..94fcfa1 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/FieldFiltersTest.java @@ -0,0 +1,41 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; + +import static de.exlll.configlib.TestUtils.assertThrowsNullPointerException; +import static de.exlll.configlib.TestUtils.getField; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +class FieldFiltersTest { + @Test + void filterDefaultRequiresNonNullStream() { + assertThrowsNullPointerException( + () -> FieldFilters.DEFAULT.test(null), + "field" + ); + } + + @Test + void filterDefault() { + class A { + class B { + private final int i = 1; + @Ignore + private int j; + private static int k; + private transient int l; + private int m; + } + } + List actual = Arrays.stream(A.B.class.getDeclaredFields()) + .filter(FieldFilters.DEFAULT) + .toList(); + List expected = List.of(getField(A.B.class, "m")); + assertThat(actual, is(expected)); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/FieldFormatterTest.java b/configlib-core/src/test/java/de/exlll/configlib/FieldFormatterTest.java new file mode 100644 index 0000000..919765c --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/FieldFormatterTest.java @@ -0,0 +1,22 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; + +import static org.mockito.Mockito.*; + +class FieldFormatterTest { + @Test + void applyCallsFormat() { + class A { + int i; + } + FieldFormatter formatter = mock(FieldFormatter.class, CALLS_REAL_METHODS); + + Field field = TestUtils.getField(A.class, "i"); + formatter.apply(field); + + verify(formatter).format(field); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/FieldFormattersTest.java b/configlib-core/src/test/java/de/exlll/configlib/FieldFormattersTest.java new file mode 100644 index 0000000..8822de4 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/FieldFormattersTest.java @@ -0,0 +1,58 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; + +import static de.exlll.configlib.TestUtils.getField; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +class FieldFormattersTest { + private static class A { + String lowercase; + String camelCase; + String withNumber123; + String with123Number; + String with_$; + } + + private static final Field f1 = getField(A.class, "lowercase"); + private static final Field f2 = getField(A.class, "camelCase"); + private static final Field f3 = getField(A.class, "withNumber123"); + private static final Field f4 = getField(A.class, "with123Number"); + private static final Field f5 = getField(A.class, "with_$"); + + @Test + void formatIdentity() { + FieldFormatters formatter = FieldFormatters.IDENTITY; + + assertThat(formatter.format(f1), is("lowercase")); + assertThat(formatter.format(f2), is("camelCase")); + assertThat(formatter.format(f3), is("withNumber123")); + assertThat(formatter.format(f4), is("with123Number")); + assertThat(formatter.format(f5), is("with_$")); + } + + @Test + void formatLowerUnderscore() { + FieldFormatters formatter = FieldFormatters.LOWER_UNDERSCORE; + + assertThat(formatter.format(f1), is("lowercase")); + assertThat(formatter.format(f2), is("camel_case")); + assertThat(formatter.format(f3), is("with_number123")); + assertThat(formatter.format(f4), is("with123_number")); + assertThat(formatter.format(f5), is("with_$")); + } + + @Test + void formatUpperUnderscore() { + FieldFormatters formatter = FieldFormatters.UPPER_UNDERSCORE; + + assertThat(formatter.format(f1), is("LOWERCASE")); + assertThat(formatter.format(f2), is("CAMEL_CASE")); + assertThat(formatter.format(f3), is("WITH_NUMBER123")); + assertThat(formatter.format(f4), is("WITH123_NUMBER")); + assertThat(formatter.format(f5), is("WITH_$")); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/FileConfigurationPropertiesTest.java b/configlib-core/src/test/java/de/exlll/configlib/FileConfigurationPropertiesTest.java new file mode 100644 index 0000000..5e4b9b7 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/FileConfigurationPropertiesTest.java @@ -0,0 +1,27 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class FileConfigurationPropertiesTest { + @Test + void builderDefaultValues() { + FileConfigurationProperties properties = FileConfigurationProperties.newBuilder().build(); + assertNull(properties.getHeader()); + assertNull(properties.getFooter()); + assertTrue(properties.createParentDirectories()); + } + + @Test + void builderCopiesValues() { + FileConfigurationProperties properties = FileConfigurationProperties.newBuilder() + .header("THE HEADER") + .footer("THE FOOTER") + .createParentDirectories(false) + .build(); + assertEquals("THE HEADER", properties.getHeader()); + assertEquals("THE FOOTER", properties.getFooter()); + assertFalse(properties.createParentDirectories()); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/ReflectTest.java b/configlib-core/src/test/java/de/exlll/configlib/ReflectTest.java new file mode 100644 index 0000000..8e991f7 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/ReflectTest.java @@ -0,0 +1,217 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; + +import static de.exlll.configlib.TestUtils.assertThrowsRuntimeException; +import static de.exlll.configlib.TestUtils.getField; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +class ReflectTest { + + static class B1 { + B1(int i) {} + } + + @Test + void newInstanceRequiresNoArgsCtor() { + assertThrowsRuntimeException( + () -> Reflect.newInstance(B1.class), + "Class B1 doesn't have a no-args constructor." + ); + } + + static abstract class B2 {} + + @Test + void newInstanceRequiresConcreteClass() { + assertThrowsRuntimeException( + () -> Reflect.newInstance(B2.class), + "Class B2 is not instantiable." + ); + } + + static class B3 { + B3() { + throw new RuntimeException(); + } + } + + @Test + void newInstanceRequiresNonThrowingCtor() { + assertThrowsRuntimeException( + () -> Reflect.newInstance(B3.class), + "Constructor of class B3 threw an exception." + ); + } + + static class B4 { + int i = 10; + } + + @Test + void newInstance() { + B4 inst = Reflect.newInstance(B4.class); + assertThat(inst, notNullValue()); + assertThat(inst.i, is(10)); + } + + @ParameterizedTest + @ValueSource(ints = {0, 1, 2}) + void newArray(int arrayLength) { + String[] strings = Reflect.newArray(String.class, arrayLength); + assertThat(strings.length, is(arrayLength)); + + int[][] ints = Reflect.newArray(int[].class, arrayLength); + assertThat(ints.length, is(arrayLength)); + } + + @Test + void getValue() { + class A { + private int i = 10; + } + int value = (int) Reflect.getValue(getField(A.class, "i"), new A()); + assertThat(value, is(10)); + } + + @Test + void setValue() { + class A { + private int i = 10; + } + A a = new A(); + Reflect.setValue(getField(A.class, "i"), a, 20); + assertThat(a.i, is(20)); + } + + @Test + void setValueDoesNotSetFinalField() { + class A { + private final int i = 10; + } + A a = new A(); + Reflect.setValue(getField(A.class, "i"), a, 20); + assertThat(a.i, is(10)); + } + + @Test + void isIntegerType() { + assertThat(Reflect.isIntegerType(byte.class), is(true)); + assertThat(Reflect.isIntegerType(Byte.class), is(true)); + assertThat(Reflect.isIntegerType(short.class), is(true)); + assertThat(Reflect.isIntegerType(Short.class), is(true)); + assertThat(Reflect.isIntegerType(int.class), is(true)); + assertThat(Reflect.isIntegerType(Integer.class), is(true)); + assertThat(Reflect.isIntegerType(long.class), is(true)); + assertThat(Reflect.isIntegerType(Long.class), is(true)); + assertThat(Reflect.isIntegerType(float.class), is(false)); + assertThat(Reflect.isIntegerType(Float.class), is(false)); + assertThat(Reflect.isIntegerType(double.class), is(false)); + assertThat(Reflect.isIntegerType(Double.class), is(false)); + assertThat(Reflect.isIntegerType(String.class), is(false)); + assertThat(Reflect.isIntegerType(Object.class), is(false)); + } + + @Test + void isFloatingPointType() { + assertThat(Reflect.isFloatingPointType(byte.class), is(false)); + assertThat(Reflect.isFloatingPointType(Byte.class), is(false)); + assertThat(Reflect.isFloatingPointType(short.class), is(false)); + assertThat(Reflect.isFloatingPointType(Short.class), is(false)); + assertThat(Reflect.isFloatingPointType(int.class), is(false)); + assertThat(Reflect.isFloatingPointType(Integer.class), is(false)); + assertThat(Reflect.isFloatingPointType(long.class), is(false)); + assertThat(Reflect.isFloatingPointType(Long.class), is(false)); + assertThat(Reflect.isFloatingPointType(float.class), is(true)); + assertThat(Reflect.isFloatingPointType(Float.class), is(true)); + assertThat(Reflect.isFloatingPointType(double.class), is(true)); + assertThat(Reflect.isFloatingPointType(Double.class), is(true)); + assertThat(Reflect.isFloatingPointType(String.class), is(false)); + assertThat(Reflect.isFloatingPointType(Object.class), is(false)); + } + + @Test + void isEnumType() { + enum A {} + class B {} + assertThat(Reflect.isEnumType(A.class), is(true)); + assertThat(Reflect.isEnumType(B.class), is(false)); + } + + @Test + void isArrayType() { + enum A {} + class B {} + assertThat(Reflect.isArrayType(A.class), is(false)); + assertThat(Reflect.isArrayType(B.class), is(false)); + assertThat(Reflect.isArrayType(int.class), is(false)); + assertThat(Reflect.isArrayType(A[].class), is(true)); + assertThat(Reflect.isArrayType(B[].class), is(true)); + assertThat(Reflect.isArrayType(int[].class), is(true)); + assertThat(Reflect.isArrayType(A[][].class), is(true)); + assertThat(Reflect.isArrayType(B[][].class), is(true)); + assertThat(Reflect.isArrayType(int[][].class), is(true)); + } + + @Test + void isListType() { + assertThat(Reflect.isListType(Object.class), is(false)); + assertThat(Reflect.isListType(HashMap.class), is(false)); + assertThat(Reflect.isListType(HashSet.class), is(false)); + assertThat(Reflect.isListType(ArrayList.class), is(true)); + } + + @Test + void isSetType() { + assertThat(Reflect.isSetType(Object.class), is(false)); + assertThat(Reflect.isSetType(HashMap.class), is(false)); + assertThat(Reflect.isSetType(HashSet.class), is(true)); + assertThat(Reflect.isSetType(ArrayList.class), is(false)); + } + + @Test + void isMapType() { + assertThat(Reflect.isMapType(Object.class), is(false)); + assertThat(Reflect.isMapType(HashMap.class), is(true)); + assertThat(Reflect.isMapType(HashSet.class), is(false)); + assertThat(Reflect.isMapType(ArrayList.class), is(false)); + } + + @Test + void isConfiguration() { + class A {} + class B extends A {} + @Configuration + class C {} + class D extends C {} + + assertThat(Reflect.isConfiguration(A.class), is(false)); + assertThat(Reflect.isConfiguration(B.class), is(false)); + assertThat(Reflect.isConfiguration(C.class), is(true)); + assertThat(Reflect.isConfiguration(D.class), is(true)); + } + + @Test + void isIgnored() { + class A { + @Ignore + private int a; + private int b; + } + + Field fieldA = getField(A.class, "a"); + Field fieldB = getField(A.class, "b"); + + assertThat(Reflect.isIgnored(fieldA), is(true)); + assertThat(Reflect.isIgnored(fieldB), is(false)); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/SerializerSelectorTest.java b/configlib-core/src/test/java/de/exlll/configlib/SerializerSelectorTest.java new file mode 100644 index 0000000..9b30956 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/SerializerSelectorTest.java @@ -0,0 +1,362 @@ +package de.exlll.configlib; + +import de.exlll.configlib.Serializers.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.awt.Point; +import java.lang.reflect.Field; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static de.exlll.configlib.TestUtils.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +class SerializerSelectorTest { + private static final SerializerSelector SELECTOR = new SerializerSelector( + ConfigurationProperties.newBuilder().build() + ); + + private static Type getGenericType(Class cls, String fieldName) { + Field ls = getField(cls, fieldName); + return ls.getGenericType(); + } + + @ParameterizedTest + @ValueSource(classes = {boolean.class, Boolean.class}) + void selectSerializerBoolean(Class cls) { + Serializer serializer = SELECTOR.select(cls); + assertThat(serializer, instanceOf(BooleanSerializer.class)); + } + + @ParameterizedTest + @ValueSource(classes = { + byte.class, Byte.class, short.class, Short.class, + int.class, Integer.class, long.class, Long.class, + float.class, Float.class, double.class, Double.class + }) + void selectSerializerNumber(Class cls) { + NumberSerializer serializer = (NumberSerializer) SELECTOR.select(cls); + assertThat(serializer.getNumberClass(), equalTo(cls)); + } + + @ParameterizedTest + @ValueSource(classes = {char.class, Character.class}) + void selectSerializerChar(Class cls) { + Serializer serializer = SELECTOR.select(cls); + assertThat(serializer, instanceOf(CharacterSerializer.class)); + } + + @Test + void selectSerializerString() { + Serializer serializer = SELECTOR.select(String.class); + assertThat(serializer, instanceOf(StringSerializer.class)); + } + + @Test + void selectSerializerBigInteger() { + Serializer serializer = SELECTOR.select(BigInteger.class); + assertThat(serializer, instanceOf(BigIntegerSerializer.class)); + } + + @Test + void selectSerializerBigDecimal() { + Serializer serializer = SELECTOR.select(BigDecimal.class); + assertThat(serializer, instanceOf(BigDecimalSerializer.class)); + } + + @Test + void selectSerializerLocalDate() { + Serializer serializer = SELECTOR.select(LocalDate.class); + assertThat(serializer, instanceOf(LocalDateSerializer.class)); + } + + @Test + void selectSerializerLocalTime() { + Serializer serializer = SELECTOR.select(LocalTime.class); + assertThat(serializer, instanceOf(LocalTimeSerializer.class)); + } + + @Test + void selectSerializerLocalDateTime() { + Serializer serializer = SELECTOR.select(LocalDateTime.class); + assertThat(serializer, instanceOf(LocalDateTimeSerializer.class)); + } + + @Test + void selectSerializerEnum() { + enum E {} + EnumSerializer serializer = (EnumSerializer) SELECTOR.select(E.class); + assertThat(serializer.getEnumCls(), equalTo(E.class)); + } + + @Test + void selectSerializerArray() { + var serializer = (ArraySerializer) SELECTOR.select(String[][].class); + assertThat(serializer.getComponentType(), equalTo(String[].class)); + + var elementSerializer = (ArraySerializer) serializer.getElementSerializer(); + assertThat(elementSerializer.getComponentType(), equalTo(String.class)); + assertThat(elementSerializer.getElementSerializer(), instanceOf(StringSerializer.class)); + } + + @Test + void selectSerializerPrimitiveBooleanArray() { + Serializer serializer = SELECTOR.select(boolean[].class); + assertThat(serializer, instanceOf(PrimitiveBooleanArraySerializer.class)); + } + + @Test + void selectSerializerPrimitiveCharacterArray() { + Serializer serializer = SELECTOR.select(char[].class); + assertThat(serializer, instanceOf(PrimitiveCharacterArraySerializer.class)); + } + + @Test + void selectSerializerPrimitiveByteArray() { + Serializer serializer = SELECTOR.select(byte[].class); + assertThat(serializer, instanceOf(PrimitiveByteArraySerializer.class)); + } + + @Test + void selectSerializerPrimitiveShortArray() { + Serializer serializer = SELECTOR.select(short[].class); + assertThat(serializer, instanceOf(PrimitiveShortArraySerializer.class)); + } + + @Test + void selectSerializerPrimitiveIntegerArray() { + Serializer serializer = SELECTOR.select(int[].class); + assertThat(serializer, instanceOf(PrimitiveIntegerArraySerializer.class)); + } + + @Test + void selectSerializerPrimitiveLongArray() { + Serializer serializer = SELECTOR.select(long[].class); + assertThat(serializer, instanceOf(PrimitiveLongArraySerializer.class)); + } + + @Test + void selectSerializerPrimitiveFloatArray() { + Serializer serializer = SELECTOR.select(float[].class); + assertThat(serializer, instanceOf(PrimitiveFloatArraySerializer.class)); + } + + @Test + void selectSerializerPrimitiveDoubleArray() { + Serializer serializer = SELECTOR.select(double[].class); + assertThat(serializer, instanceOf(PrimitiveDoubleArraySerializer.class)); + } + + @Test + void selectSerializerConfiguration() { + @Configuration + class A { + int i; + } + var serializer = (ConfigurationSerializer) SELECTOR.select(A.class); + assertThat(serializer.getConfigurationType(), equalTo(A.class)); + } + + @Test + void selectSerializerMissingType() { + assertThrowsConfigurationException( + () -> SELECTOR.select(Object.class), + "Missing serializer for type class java.lang.Object.\nEither annotate the type with " + + "@Configuration or provide a custom serializer by adding it to the properties." + ); + } + + @Test + void selectSerializerCustomType() { + var properties = ConfigurationProperties.newBuilder() + .addSerializer(Point.class, POINT_SERIALIZER) + .build(); + SerializerSelector selector = new SerializerSelector(properties); + var pointSerializer = selector.select(Point.class); + assertThat(pointSerializer, sameInstance(POINT_SERIALIZER)); + } + + @Test + void selectSerializerCustomSerializerTakesPrecedence() { + var properties = ConfigurationProperties.newBuilder() + .addSerializer(BigInteger.class, CUSTOM_BIG_INTEGER_SERIALIZER) + .build(); + SerializerSelector selector = new SerializerSelector(properties); + var bigIntegerSerializer = selector.select(BigInteger.class); + assertThat(bigIntegerSerializer, instanceOf(TestUtils.CustomBigIntegerSerializer.class)); + assertThat(bigIntegerSerializer, sameInstance(CUSTOM_BIG_INTEGER_SERIALIZER)); + } + + @Test + void selectSerializerList() { + class A { + List ls; + } + var serializer = (ListSerializer) SELECTOR.select(getGenericType(A.class, "ls")); + assertThat(serializer.getElementSerializer(), instanceOf(StringSerializer.class)); + } + + @Test + void selectSerializerListNested() { + class A { + List> lls; + } + var serializer = (ListSerializer) SELECTOR.select(getGenericType(A.class, "lls")); + var elementSerializer = (ListSerializer) serializer.getElementSerializer(); + assertThat(elementSerializer.getElementSerializer(), instanceOf(StringSerializer.class)); + } + + @Test + void selectSerializerSetsAsSets() { + class A { + Set ss; + } + SerializerSelector selector = new SerializerSelector( + ConfigurationProperties.newBuilder().serializeSetsAsLists(false).build() + ); + var serializer = (SetSerializer) selector.select(getGenericType(A.class, "ss")); + assertThat(serializer.getElementSerializer(), instanceOf(StringSerializer.class)); + } + + @Test + void selectSerializerSetsAsLists() { + class A { + Set ss; + } + var serializer = (SetAsListSerializer) SELECTOR.select(getGenericType(A.class, "ss")); + assertThat(serializer.getElementSerializer(), instanceOf(StringSerializer.class)); + } + + @Test + void selectSerializerMap() { + class A { + Map mis; + } + var serializer = (MapSerializer) SELECTOR.select(getGenericType(A.class, "mis")); + var numberSerializer = (NumberSerializer) serializer.getKeySerializer(); + assertThat(numberSerializer.getNumberClass(), equalTo(Integer.class)); + assertThat(serializer.getValueSerializer(), instanceOf(StringSerializer.class)); + } + + @Test + void selectSerializerMapNested() { + enum E {} + class A { + Map>> mesle; + } + var serializer = (MapSerializer) SELECTOR.select(getGenericType(A.class, "mesle")); + + var keySerializer = (EnumSerializer) serializer.getKeySerializer(); + assertThat(keySerializer.getEnumCls(), equalTo(E.class)); + + var valSerializer = (SetAsListSerializer) serializer.getValueSerializer(); + var listSerializer = (ListSerializer) valSerializer.getElementSerializer(); + var enumSerializer = (EnumSerializer) listSerializer.getElementSerializer(); + assertThat(enumSerializer.getEnumCls(), equalTo(E.class)); + } + + @Test + void selectSerializerMapInvalidKeyType1() { + class A { + Map, String> mlss; + } + Type type = getGenericType(A.class, "mlss"); + assertThrowsConfigurationException( + () -> SELECTOR.select(type), + "Cannot select serializer for type '" + type + "'.\nMap keys can only be " + + "of simple or enum type." + ); + } + + @Test + void selectSerializerMapInvalidKeyType2() { + class A { + Map mps; + } + Type type = getGenericType(A.class, "mps"); + assertThrowsConfigurationException( + () -> SELECTOR.select(type), + "Cannot select serializer for type '" + type + "'.\nMap keys can only be " + + "of simple or enum type." + ); + } + + @Test + void selectSerializerOtherParameterizedType() { + class Box {} + class A { + Box box; + } + Type type = getGenericType(A.class, "box"); + assertThrowsConfigurationException( + () -> SELECTOR.select(type), + "Cannot select serializer for type '" + type + "'.\nParameterized " + + "types other than lists, sets, and maps cannot be serialized." + ); + } + + @Test + void selectSerializerGenericArrayType() { + class A { + List[] ga; + } + Type type = getGenericType(A.class, "ga"); + assertThrowsConfigurationException( + () -> SELECTOR.select(type), + "Cannot select serializer for type 'java.util.List[]'.\n" + + "Generic array types cannot be serialized." + ); + } + + @Test + void selectSerializerBoundedWildcardType() { + class A { + List les; + } + ParameterizedType ptype = (ParameterizedType) getGenericType(A.class, "les"); + Type type = ptype.getActualTypeArguments()[0]; + assertThrowsConfigurationException( + () -> SELECTOR.select(type), + "Cannot select serializer for type '? extends java.lang.String'.\n" + + "Wildcard types cannot be serialized." + ); + } + + @Test + void selectSerializerWildcardType() { + class A { + List lw; + } + ParameterizedType ptype = (ParameterizedType) getGenericType(A.class, "lw"); + Type type = ptype.getActualTypeArguments()[0]; + assertThrowsConfigurationException( + () -> SELECTOR.select(type), + "Cannot select serializer for type '?'.\n" + + "Wildcard types cannot be serialized." + ); + } + + @Test + void selectSerializerTypeVariable() { + class A { + T t; + } + Type type = getGenericType(A.class, "t"); + assertThrowsConfigurationException( + () -> SELECTOR.select(type), + "Cannot select serializer for type 'T'.\n" + + "Type variables cannot be serialized." + ); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/SerializersTest.java b/configlib-core/src/test/java/de/exlll/configlib/SerializersTest.java new file mode 100644 index 0000000..6146758 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/SerializersTest.java @@ -0,0 +1,908 @@ +package de.exlll.configlib; + +import de.exlll.configlib.Serializers.MapSerializer; +import de.exlll.configlib.Serializers.NumberSerializer; +import de.exlll.configlib.Serializers.SetAsListSerializer; +import de.exlll.configlib.Serializers.SetSerializer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static de.exlll.configlib.TestUtils.*; +import static java.util.Arrays.asList; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; + +class SerializersTest { + @Test + void booleanSerializer() { + Serializer serializer = new Serializers.BooleanSerializer(); + + Boolean element = true; + + Object serialized = serializer.serialize(element); + assertThat(serialized, sameInstance(element)); + + Object deserialized = serializer.deserialize(element); + assertThat(deserialized, sameInstance(element)); + } + + @Test + void numberSerializerRequiresValidNumberType() { + assertThrowsNullPointerException( + () -> new NumberSerializer(null), + "number class" + ); + assertThrowsIllegalArgumentException( + () -> new NumberSerializer(BigInteger.class), + "Class BigInteger is not a byte, short, int, long, float, double, or a " + + "wrapper type of one of the primitive number types." + ); + } + + @Test + void numberSerializerDeserializeInvalidType() { + NumberSerializer serializer = new NumberSerializer(int.class); + + assertThrowsConfigurationException( + () -> serializer.deserialize(BigInteger.ONE), + "Cannot deserialize element '1' of type BigInteger.\n" + + "This serializer only supports primitive number types and their wrapper types." + ); + } + + @ParameterizedTest + @ValueSource(classes = { + byte.class, Byte.class, + short.class, Short.class, + int.class, Integer.class, + long.class, Long.class + }) + void numberSerializerSerializeIntegerType(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + int[] numbers = {Integer.MIN_VALUE, 0, Integer.MAX_VALUE}; + + for (int number : numbers) { + Number serialized = serializer.serialize(number); + assertThat(serialized, instanceOf(Long.class)); + assertThat(serialized, is((long) number)); + } + } + + @ParameterizedTest + @ValueSource(classes = { + float.class, Float.class, + double.class, Double.class + }) + void numberSerializerSerializeFloatingPointType(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + float[] numbers = {-Float.MAX_VALUE, -Float.MIN_VALUE, 0, Float.MIN_VALUE, Float.MAX_VALUE}; + + for (float number : numbers) { + Number serialized = serializer.serialize(number); + assertThat(serialized, instanceOf(Double.class)); + assertThat(serialized, is((double) number)); + } + } + + @ParameterizedTest + @ValueSource(classes = { + byte.class, Byte.class, + short.class, Short.class, + int.class, Integer.class, + long.class, Long.class + }) + void numberSerializerDeserializeByteToIntegerType(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + byte[] numbers = {Byte.MIN_VALUE, 0, Byte.MAX_VALUE}; + + for (byte number : numbers) { + Number deserialized = serializer.deserialize(number); + assertThat(deserialized, instanceOf(cls)); + assertThat(deserialized.longValue(), is((long) number)); + } + } + + @ParameterizedTest + @ValueSource(classes = { + short.class, Short.class, + int.class, Integer.class, + long.class, Long.class + }) + void numberSerializerDeserializeShortToIntegerType(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + short[] numbers = {Short.MIN_VALUE, 0, Short.MAX_VALUE}; + + for (short number : numbers) { + Number deserialized = serializer.deserialize(number); + assertThat(deserialized, instanceOf(cls)); + assertThat(deserialized.longValue(), is((long) number)); + } + } + + @ParameterizedTest + @ValueSource(classes = {int.class, Integer.class, long.class, Long.class}) + void numberSerializerDeserializeIntegerToIntegerType(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + int[] numbers = {Integer.MIN_VALUE, 0, Integer.MAX_VALUE}; + + for (int number : numbers) { + Number deserialized = serializer.deserialize(number); + assertThat(deserialized, instanceOf(cls)); + assertThat(deserialized.longValue(), is((long) number)); + } + } + + @ParameterizedTest + @ValueSource(classes = {long.class, Long.class}) + void numberSerializerDeserializeLongToIntegerType(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + long[] numbers = {Long.MIN_VALUE, 0, Long.MAX_VALUE}; + + for (long number : numbers) { + Number deserialized = serializer.deserialize(number); + assertThat(deserialized, instanceOf(cls)); + assertThat(deserialized, is(number)); + } + } + + @ParameterizedTest + @ValueSource(classes = {byte.class, Byte.class}) + void numberSerializerDeserializeTooLargeIntegerTypeToByte(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + long[] values = { + Long.MIN_VALUE, Integer.MIN_VALUE, Short.MIN_VALUE, Byte.MIN_VALUE - 1, + Long.MAX_VALUE, Integer.MAX_VALUE, Short.MAX_VALUE, Byte.MAX_VALUE + 1 + }; + for (long value : values) { + String msg = "Number " + value + " cannot be converted to type " + cls.getSimpleName() + + ". It does not fit into the range of valid values [-128, 127]."; + assertThrowsConfigurationException(() -> serializer.deserialize(value), msg); + } + } + + @ParameterizedTest + @ValueSource(classes = {short.class, Short.class}) + void numberSerializerDeserializeTooLargeIntegerTypeToShort(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + long[] values = { + Long.MIN_VALUE, Integer.MIN_VALUE, Short.MIN_VALUE - 1, + Long.MAX_VALUE, Integer.MAX_VALUE, Short.MAX_VALUE + 1, + }; + for (long value : values) { + String msg = "Number " + value + " cannot be converted to type " + cls.getSimpleName() + + ". It does not fit into the range of valid values [-32768, 32767]."; + assertThrowsConfigurationException(() -> serializer.deserialize(value), msg); + } + } + + @ParameterizedTest + @ValueSource(classes = {int.class, Integer.class}) + void numberSerializerDeserializeTooLargeIntegerTypeToInteger(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + long[] values = { + Long.MIN_VALUE, Integer.MIN_VALUE - 1L, + Long.MAX_VALUE, Integer.MAX_VALUE + 1L, + }; + for (long value : values) { + String msg = "Number " + value + " cannot be converted to type " + cls.getSimpleName() + + ". It does not fit into the range of valid values [-2147483648, 2147483647]."; + assertThrowsConfigurationException(() -> serializer.deserialize(value), msg); + } + } + + @ParameterizedTest + @ValueSource(classes = {float.class, Float.class, double.class, Double.class}) + void numberSerializerDeserializeFloatToFloatingPointType(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + float[] numbers = {-Float.MAX_VALUE, -Float.MIN_VALUE, 0, Float.MIN_VALUE, Float.MAX_VALUE}; + + for (float number : numbers) { + Number deserialized = serializer.deserialize(number); + assertThat(deserialized, instanceOf(cls)); + assertThat(deserialized.doubleValue(), is((double) number)); + } + } + + @ParameterizedTest + @ValueSource(classes = {double.class, Double.class}) + void numberSerializerDeserializeDoubleToFloatingPointType(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + double[] numbers = {-Double.MAX_VALUE, -Double.MIN_VALUE, 0, Double.MIN_VALUE, Double.MAX_VALUE}; + + for (double number : numbers) { + Number deserialized = serializer.deserialize(number); + assertThat(deserialized, instanceOf(cls)); + assertThat(deserialized, is(number)); + } + } + + @ParameterizedTest + @ValueSource(classes = {float.class, Float.class}) + void numberSerializerDeserializeTooLargeFloatingPointTypeToFloat(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + double ulpFloatMax = Math.ulp((double) Float.MAX_VALUE); + double[] values = { + +Double.MAX_VALUE, + -Double.MAX_VALUE, + (double) +Float.MAX_VALUE + ulpFloatMax, + (double) -Float.MAX_VALUE - ulpFloatMax + }; + for (double value : values) { + String clsName = cls.getSimpleName(); + String msg = "Number " + value + " cannot be converted to type " + clsName + ". " + + "It is larger than the largest possible " + clsName + " value."; + assertThrowsConfigurationException(() -> serializer.deserialize(value), msg); + } + } + + @ParameterizedTest + @ValueSource(classes = {float.class, Float.class}) + void numberSerializerDeserializeTooSmallFloatingPointTypeToFloat(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + double ulpFloatMin = Math.ulp((double) Float.MIN_VALUE); + double[] values = { + +Double.MIN_VALUE, + -Double.MIN_VALUE, + (double) +Float.MIN_VALUE - ulpFloatMin, + (double) -Float.MIN_VALUE + ulpFloatMin, + }; + for (double value : values) { + String clsName = cls.getSimpleName(); + String msg = "Number " + value + " cannot be converted to type " + clsName + ". " + + "It is smaller than the smallest possible " + clsName + " value."; + assertThrowsConfigurationException(() -> serializer.deserialize(value), msg); + } + } + + @ParameterizedTest + @ValueSource(classes = {float.class, Float.class}) + void numberSerializerDeserializeFloatSpecialValues(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + numberSerializerDeserializeFloatingPointSpecialValues( + serializer, + Float.class, + Float.POSITIVE_INFINITY, + Float.NEGATIVE_INFINITY, + Float.NaN + ); + } + + @ParameterizedTest + @ValueSource(classes = {double.class, Double.class}) + void numberSerializerDeserializeDoubleSpecialValues(Class cls) { + NumberSerializer serializer = new NumberSerializer(cls); + + numberSerializerDeserializeFloatingPointSpecialValues( + serializer, + Double.class, + Double.POSITIVE_INFINITY, + Double.NEGATIVE_INFINITY, + Double.NaN + ); + } + + private void numberSerializerDeserializeFloatingPointSpecialValues( + NumberSerializer serializer, + Class type, + T positiveInfinity, + T negativeInfinity, + T nan + ) { + Number pinfDouble = serializer.deserialize(Double.POSITIVE_INFINITY); + Number ninfDouble = serializer.deserialize(Double.NEGATIVE_INFINITY); + Number nanDouble = serializer.deserialize(Double.NaN); + + Number pinfFloat = serializer.deserialize(Float.POSITIVE_INFINITY); + Number ninfFloat = serializer.deserialize(Float.NEGATIVE_INFINITY); + Number nanFloat = serializer.deserialize(Float.NaN); + + assertThat(pinfDouble, instanceOf(type)); + assertThat(ninfDouble, instanceOf(type)); + assertThat(nanDouble, instanceOf(type)); + + assertThat(pinfFloat, instanceOf(type)); + assertThat(ninfFloat, instanceOf(type)); + assertThat(nanFloat, instanceOf(type)); + + assertThat(pinfDouble, is(positiveInfinity)); + assertThat(ninfDouble, is(negativeInfinity)); + assertThat(nanDouble, is(nan)); + + assertThat(pinfFloat, is(positiveInfinity)); + assertThat(ninfFloat, is(negativeInfinity)); + assertThat(nanFloat, is(nan)); + } + + @Test + void stringSerializer() { + Serializer serializer = new Serializers.StringSerializer(); + + String random = "RANDOM"; + + assertThat(serializer.serialize(random), sameInstance(random)); + assertThat(serializer.deserialize(random), sameInstance(random)); + } + + @Test + void characterSerializer() { + Serializer serializer = new Serializers.CharacterSerializer(); + + assertThat(serializer.serialize('c'), is("c")); + assertThat(serializer.deserialize("c"), is('c')); + } + + @Test + void characterSerializerDeserializeInvalidString() { + Serializer serializer = new Serializers.CharacterSerializer(); + assertThrowsConfigurationException( + () -> serializer.deserialize(""), + "An empty string cannot be converted to a character." + ); + assertThrowsConfigurationException( + () -> serializer.deserialize("ab"), + "String 'ab' is too long to be converted to a character." + ); + } + + @Test + void bigIntegerSerializer() { + Serializer serializer = new Serializers.BigIntegerSerializer(); + + final String result = "18446744073709551614"; + + BigInteger integer = BigInteger.valueOf(Long.MAX_VALUE).multiply(BigInteger.TWO); + assertThat(serializer.serialize(integer), is(result)); + + integer = new BigInteger(result); + assertThat(serializer.deserialize(result), is(integer)); + } + + @Test + void bigDecimalSerializer() { + Serializer serializer = new Serializers.BigDecimalSerializer(); + + final String result = "3.23170060713109998320439596646649E+616"; + final BigDecimal doubleMax = BigDecimal.valueOf(Double.MAX_VALUE); + + BigDecimal decimal = doubleMax.multiply(doubleMax); + + assertThat(serializer.serialize(decimal), is(result)); + decimal = new BigDecimal(result); + assertThat(serializer.deserialize(result), is(decimal)); + } + + + @Test + void localDateSerializer() { + Serializer serializer = new Serializers.LocalDateSerializer(); + + LocalDate date = LocalDate.of(2000, Month.FEBRUARY, 29); + + assertThat(serializer.serialize(date), is("2000-02-29")); + assertThat(serializer.deserialize("2000-02-29"), is(date)); + } + + @Test + void localTimeSerializer() { + Serializer serializer = new Serializers.LocalTimeSerializer(); + + LocalTime time = LocalTime.of(10, 11, 12, 13); + + assertThat(serializer.serialize(time), is("10:11:12")); + + time = time.truncatedTo(ChronoUnit.SECONDS); + assertThat(serializer.deserialize("10:11:12"), is(time)); + } + + @Test + void localDateTimeSerializer() { + Serializer serializer = new Serializers.LocalDateTimeSerializer(); + + LocalDateTime dateTime = LocalDateTime.of(2000, Month.FEBRUARY, 29, 10, 11, 12, 13); + + assertThat(serializer.serialize(dateTime), is("2000-02-29T10:11:12")); + + dateTime = dateTime.truncatedTo(ChronoUnit.SECONDS); + assertThat(serializer.deserialize("2000-02-29T10:11:12"), is(dateTime)); + } + + enum E {X, Y, Z} + + @ParameterizedTest + @EnumSource(E.class) + void enumSerializer(Enum e) { + Serializers.EnumSerializer serializer = new Serializers.EnumSerializer(E.class); + + assertThat(serializer.serialize(e), is(e.name())); + assertThat(serializer.deserialize(e.name()), is(e)); + } + + @Test + void enumSerializerMissingValue() { + Serializers.EnumSerializer serializer = new Serializers.EnumSerializer(E.class); + + assertThrowsConfigurationException( + () -> serializer.deserialize("A"), + "Enum class E does not contain enum 'A'. Valid values are: [X, Y, Z]" + ); + } + + @Test + void listSerializer() { + var serializer = new Serializers.ListSerializer<>(StringToIntSerializer.INSTANCE, false, false); + + List integers = serializer.serialize(List.of("1", "2", "3")); + assertThat(integers, is(List.of(1, 2, 3))); + + List strings = serializer.deserialize(List.of(4, 5, 6)); + assertThat(strings, is(List.of("4", "5", "6"))); + } + + @Test + void listSerializerNoNulls() { + var serializer = new Serializers.ListSerializer<>(StringToIntSerializer.INSTANCE, false, false); + + List integers = serializer.serialize(asList("1", null, "2", null, "3")); + assertThat(integers, is(List.of(1, 2, 3))); + + List strings = serializer.deserialize(asList(4, null, 5, null, 6)); + assertThat(strings, is(List.of("4", "5", "6"))); + } + + @Test + void listSerializerOutputNulls() { + var serializer = new Serializers.ListSerializer<>(StringToIntSerializer.INSTANCE, true, false); + + List actual = serializer.serialize(asList("1", null, "3")); + assertThat(actual, is(asList(1, null, 3))); + } + + @Test + void listSerializerInputNulls() { + var serializer = new Serializers.ListSerializer<>(StringToIntSerializer.INSTANCE, false, true); + + List actual = serializer.deserialize(asList(4, null, 5)); + assertThat(actual, is(asList("4", null, "5"))); + } + + @Test + void setSerializer() { + var serializer = new SetSerializer<>(StringToIntSerializer.INSTANCE, false, false); + + Set integers = serializer.serialize(Set.of("1", "2", "3")); + assertThat(integers, is(Set.of(1, 2, 3))); + + Set strings = serializer.deserialize(Set.of(4, 5, 6)); + assertThat(strings, is(Set.of("4", "5", "6"))); + } + + @Test + void setSerializerNoNulls() { + var serializer = new SetSerializer<>(StringToIntSerializer.INSTANCE, false, false); + + Set integers = serializer.serialize(asSet("1", null, "2", null, "3")); + assertThat(integers, is(asSet(1, 2, 3))); + + Set strings = serializer.deserialize(asSet(4, null, 5, null, 6)); + assertThat(strings, is(asSet("4", "5", "6"))); + } + + @Test + void setSerializerOutputNulls() { + var serializer = new SetSerializer<>(StringToIntSerializer.INSTANCE, true, false); + + Set actual = serializer.serialize(asSet("1", null, "3")); + assertThat(actual, is(asSet(1, null, 3))); + } + + @Test + void setSerializerInputNulls() { + var serializer = new SetSerializer<>(StringToIntSerializer.INSTANCE, false, true); + + Set actual = serializer.deserialize(asSet(4, null, 5)); + assertThat(actual, is(asSet("4", null, "5"))); + } + + + @Test + void setAsListSerializer() { + var serializer = new SetAsListSerializer<>(StringToIntSerializer.INSTANCE, false, false); + + List integers = serializer.serialize(asSet("1", "2", "3")); + assertThat(integers, is(asList(1, 2, 3))); + + Set strings = serializer.deserialize(List.of(4, 5, 6)); + assertThat(strings, is(Set.of("4", "5", "6"))); + } + + @Test + void setAsListSerializerNoNulls() { + var serializer = new SetAsListSerializer<>(StringToIntSerializer.INSTANCE, false, false); + + List integers = serializer.serialize(asSet("1", null, "2", null, "3")); + assertThat(integers, is(asList(1, 2, 3))); + + Set strings = serializer.deserialize(asList(4, null, 5, null, 6)); + assertThat(strings, is(asSet("4", "5", "6"))); + } + + @Test + void setAsListSerializerOutputNulls() { + var serializer = new SetAsListSerializer<>(StringToIntSerializer.INSTANCE, true, false); + + List actual = serializer.serialize(asSet("1", null, "3")); + assertThat(actual, is(asList(1, null, 3))); + } + + @Test + void setAsListSerializerInputNulls() { + var serializer = new SetAsListSerializer<>(StringToIntSerializer.INSTANCE, false, true); + + Set actual = serializer.deserialize(asList(4, null, 5)); + assertThat(actual, is(asSet("4", null, "5"))); + } + + @Test + void mapSerializer() { + var serializer = new MapSerializer<>( + StringToIntSerializer.INSTANCE, + StringToIntSerializer.INSTANCE, + false, + false + ); + + Map integers = serializer.serialize(Map.of("1", "2", "3", "4")); + assertThat(integers, is(Map.of(1, 2, 3, 4))); + + Map strings = serializer.deserialize(Map.of(5, 6, 7, 8)); + assertThat(strings, is(Map.of("5", "6", "7", "8"))); + } + + @Test + void mapSerializerNoNulls() { + var serializer = new MapSerializer<>( + StringToIntSerializer.INSTANCE, + StringToIntSerializer.INSTANCE, + false, + false + ); + Map integers = serializer.serialize(entriesAsMap( + entry("1", "2"), + entry(null, "4"), + entry("5", null), + entry(null, null), + entry("9", "10") + )); + assertThat(integers, is(Map.of(1, 2, 9, 10))); + + Map strings = serializer.deserialize(entriesAsMap( + entry(11, 12), + entry(null, 14), + entry(15, null), + entry(null, null), + entry(19, 20) + )); + assertThat(strings, is(Map.of("11", "12", "19", "20"))); + } + + @Test + void mapSerializerOutputNulls() { + var serializer = new MapSerializer<>( + StringToIntSerializer.INSTANCE, + StringToIntSerializer.INSTANCE, + true, + false + ); + Map actual = serializer.serialize(entriesAsMap( + entry("1", "2"), + entry(null, "4"), + entry("5", null), + entry(null, null), + entry("9", "10") + )); + Map expected = entriesAsMap( + entry(1, 2), + entry(5, null), + entry(null, null), + entry(9, 10) + ); + assertThat(actual, is(expected)); + } + + @Test + void mapSerializerInputNulls() { + var serializer = new MapSerializer<>( + StringToIntSerializer.INSTANCE, + StringToIntSerializer.INSTANCE, + false, + true + ); + Map actual = serializer.deserialize(entriesAsMap( + entry(11, 12), + entry(null, 14), + entry(15, null), + entry(null, null), + entry(19, 20) + )); + Map expected = entriesAsMap( + entry("11", "12"), + entry("15", null), + entry(null, null), + entry("19", "20") + ); + assertThat(actual, is(expected)); + } + + @Test + void arraySerializer() { + var serializer = new Serializers.ArraySerializer<>( + String.class, + StringToIntSerializer.INSTANCE, + false, false + ); + + List integers = serializer.serialize(new String[]{"1", "2", "3"}); + assertThat(integers, is(List.of(1, 2, 3))); + + String[] strings = serializer.deserialize(List.of(4, 5, 6)); + assertThat(strings, is(new String[]{"4", "5", "6"})); + } + + @Test + void arraySerializerNested() { + var serializer = new Serializers.ArraySerializer<>( + String[].class, + new Serializers.ArraySerializer<>( + String.class, + StringToIntSerializer.INSTANCE, + false, false + ), + false, false + ); + + String[][] input = {{"1"}, {"2", "3"}}; + List> integers = serializer.serialize(input); + assertThat(integers, is(List.of(List.of(1), List.of(2, 3)))); + + String[][] output = serializer.deserialize(List.of( + List.of(4), + List.of(5, 6) + )); + assertThat(output, is(new String[][]{{"4"}, {"5", "6"}})); + } + + @Test + void arraySerializerNoNulls() { + var serializer = new Serializers.ArraySerializer<>( + String.class, + StringToIntSerializer.INSTANCE, + false, false + ); + + List integers = serializer.serialize(new String[]{"1", null, "2", null, "3"}); + assertThat(integers, is(List.of(1, 2, 3))); + + String[] strings = serializer.deserialize(asList(4, null, 5, null, 6)); + assertThat(strings, is(new String[]{"4", "5", "6"})); + } + + @Test + void arraySerializerOutputNulls() { + var serializer = new Serializers.ArraySerializer<>( + String.class, + StringToIntSerializer.INSTANCE, + true, false + ); + + List integers = serializer.serialize(new String[]{"1", null, "2", null, "3"}); + assertThat(integers, is(asList(1, null, 2, null, 3))); + } + + @Test + void arraySerializerInputNulls() { + var serializer = new Serializers.ArraySerializer<>( + String.class, + StringToIntSerializer.INSTANCE, + false, true + ); + + String[] strings = serializer.deserialize(asList(4, null, 5, null, 6)); + assertThat(strings, is(new String[]{"4", null, "5", null, "6"})); + } + + @Test + void primitiveBooleanArraySerializer() { + var serializer = new Serializers.PrimitiveBooleanArraySerializer(); + + List list = serializer.serialize(new boolean[]{true, false, true}); + assertThat(list, is(List.of(true, false, true))); + + boolean[] array = (boolean[]) serializer.deserialize(List.of(false, true, false)); + assertThat(array, is(new boolean[]{false, true, false})); + } + + @Test + void primitiveCharacterArraySerializer() { + var serializer = new Serializers.PrimitiveCharacterArraySerializer(); + + List list = serializer.serialize(new char[]{'a', 'b', 'c'}); + assertThat(list, is(List.of("a", "b", "c"))); + + char[] array = (char[]) serializer.deserialize(List.of("d", "e", "f")); + assertThat(array, is(new char[]{'d', 'e', 'f'})); + } + + @Test + void primitiveByteArraySerializer() { + var serializer = new Serializers.PrimitiveByteArraySerializer(); + + List list = serializer.serialize(new byte[]{(byte) 1, (byte) 2, (byte) 3}); + assertThat(list, is(List.of(1L, 2L, 3L))); + + byte[] array = (byte[]) serializer.deserialize(List.of(3L, 4L, 5L)); + assertThat(array, is(new byte[]{(byte) 3, (byte) 4, (byte) 5})); + } + + @Test + void primitiveShortArraySerializer() { + var serializer = new Serializers.PrimitiveShortArraySerializer(); + + List list = serializer.serialize(new short[]{(short) 1, (short) 2, (short) 3}); + assertThat(list, is(List.of(1L, 2L, 3L))); + + short[] array = (short[]) serializer.deserialize(List.of(3L, 4L, 5L)); + assertThat(array, is(new short[]{(short) 3, (short) 4, (short) 5})); + } + + @Test + void primitiveIntegerArraySerializer() { + var serializer = new Serializers.PrimitiveIntegerArraySerializer(); + + List list = serializer.serialize(new int[]{1, 2, 3}); + assertThat(list, is(List.of(1L, 2L, 3L))); + + int[] array = (int[]) serializer.deserialize(List.of(3L, 4L, 5L)); + assertThat(array, is(new int[]{3, 4, 5})); + } + + @Test + void primitiveLongArraySerializer() { + var serializer = new Serializers.PrimitiveLongArraySerializer(); + + List list = serializer.serialize(new long[]{1L, 2L, 3L}); + assertThat(list, is(List.of(1L, 2L, 3L))); + + long[] array = (long[]) serializer.deserialize(List.of(3L, 4L, 5L)); + assertThat(array, is(new long[]{3L, 4L, 5L})); + } + + @Test + void primitiveFloatArraySerializer() { + var serializer = new Serializers.PrimitiveFloatArraySerializer(); + + List list = serializer.serialize(new float[]{1f, 2f, 3f}); + assertThat(list, is(List.of(1d, 2d, 3d))); + + float[] array = (float[]) serializer.deserialize(List.of(3d, 4d, 5d)); + assertThat(array, is(new float[]{3f, 4f, 5f})); + } + + @Test + void primitiveDoubleArraySerializer() { + var serializer = new Serializers.PrimitiveDoubleArraySerializer(); + + List list = serializer.serialize(new double[]{1d, 2d, 3d}); + assertThat(list, is(List.of(1d, 2d, 3d))); + + double[] array = (double[]) serializer.deserialize(List.of(3d, 4d, 5d)); + assertThat(array, is(new double[]{3d, 4d, 5d})); + } + + @Test + void primitiveBooleanArraySerializerDeserializeNullElement() { + var serializer = new Serializers.PrimitiveBooleanArraySerializer(); + assertThrowsConfigurationException( + () -> serializer.deserialize(asList(true, false, null)), + "The boolean element at index 2 must not be null." + ); + } + + @Test + void primitiveCharacterArraySerializerDeserializeNullElement() { + var serializer = new Serializers.PrimitiveCharacterArraySerializer(); + assertThrowsConfigurationException( + () -> serializer.deserialize(asList("a", null, "b")), + "The char element at index 1 must not be null." + ); + } + + @Test + void primitiveByteArraySerializerDeserializeNullElement() { + var serializer = new Serializers.PrimitiveByteArraySerializer(); + assertThrowsConfigurationException( + () -> serializer.deserialize(asList((byte) 1, (byte) 2, null)), + "The byte element at index 2 must not be null." + ); + } + + @Test + void primitiveShortArraySerializerDeserializeNullElement() { + var serializer = new Serializers.PrimitiveShortArraySerializer(); + assertThrowsConfigurationException( + () -> serializer.deserialize(asList((short) 1, null, (short) 2)), + "The short element at index 1 must not be null." + ); + } + + @Test + void primitiveIntegerArraySerializerDeserializeNullElement() { + var serializer = new Serializers.PrimitiveIntegerArraySerializer(); + assertThrowsConfigurationException( + () -> serializer.deserialize(asList(1, 2, null)), + "The int element at index 2 must not be null." + ); + } + + @Test + void primitiveLongArraySerializerDeserializeNullElement() { + var serializer = new Serializers.PrimitiveLongArraySerializer(); + assertThrowsConfigurationException( + () -> serializer.deserialize(asList(1L, null, 2L)), + "The long element at index 1 must not be null." + ); + } + + @Test + void primitiveFloatArraySerializerDeserializeNullElement() { + var serializer = new Serializers.PrimitiveFloatArraySerializer(); + assertThrowsConfigurationException( + () -> serializer.deserialize(asList(1f, 2f, null)), + "The float element at index 2 must not be null." + ); + } + + @Test + void primitiveDoubleArraySerializerDeserializeNullElement() { + var serializer = new Serializers.PrimitiveDoubleArraySerializer(); + assertThrowsConfigurationException( + () -> serializer.deserialize(asList(1d, null, 2d)), + "The double element at index 1 must not be null." + ); + } + + private static final class StringToIntSerializer implements Serializer { + private static final StringToIntSerializer INSTANCE = new StringToIntSerializer(); + + @Override + public Integer serialize(String element) { + return Integer.valueOf(element); + } + + @Override + public String deserialize(Integer element) { + return element.toString(); + } + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/TestUtils.java b/configlib-core/src/test/java/de/exlll/configlib/TestUtils.java new file mode 100644 index 0000000..cbcfe27 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/TestUtils.java @@ -0,0 +1,229 @@ +package de.exlll.configlib; + +import org.junit.jupiter.api.function.Executable; + +import java.awt.Point; +import java.io.IOException; +import java.lang.reflect.Field; +import java.math.BigInteger; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static java.util.stream.Collectors.joining; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public final class TestUtils { + public static final PointSerializer POINT_SERIALIZER = new PointSerializer(); + public static final PointIdentitySerializer POINT_IDENTITY_SERIALIZER = + new PointIdentitySerializer(); + public static final CustomBigIntegerSerializer CUSTOM_BIG_INTEGER_SERIALIZER + = new CustomBigIntegerSerializer(); + + public static Field getField(Class cls, String fieldName) { + return Arrays.stream(cls.getDeclaredFields()) + .filter(field -> field.getName().equals(fieldName)) + .findAny() + .orElseThrow(); + } + + public static void assertThrowsNullPointerException(Executable executable, String argumentName) { + String msg = "The " + argumentName + " must not be null."; + assertThrowsException(NullPointerException.class, executable, msg); + } + + public static void assertThrowsIllegalArgumentException( + Executable executable, + String expectedExceptionMessage + ) { + assertThrowsException(IllegalArgumentException.class, executable, expectedExceptionMessage); + } + + public static void assertThrowsConfigurationException( + Executable executable, + String expectedExceptionMessage + ) { + assertThrowsException(ConfigurationException.class, executable, expectedExceptionMessage); + } + + public static void assertThrowsRuntimeException( + Executable executable, + String expectedExceptionMessage + ) { + assertThrowsException(RuntimeException.class, executable, expectedExceptionMessage); + } + + public static void assertThrowsException( + Class exceptionType, + Executable executable, + String expectedExceptionMessage + ) { + T exception = assertThrows(exceptionType, executable); + assertEquals(expectedExceptionMessage, exception.getMessage()); + } + + public static final class CustomBigIntegerSerializer implements Serializer { + + @Override + public String serialize(BigInteger element) { + return element.multiply(BigInteger.TWO).toString(); + } + + @Override + public BigInteger deserialize(String element) { + return new BigInteger(element).divide(BigInteger.TWO); + } + } + + public static final class PointSerializer implements Serializer { + @Override + public String serialize(Point element) { + return element.x + ":" + element.y; + } + + @Override + public Point deserialize(String element) { + String[] parts = element.split(":"); + int x = Integer.parseInt(parts[0]); + int y = Integer.parseInt(parts[1]); + return new Point(x, y); + } + } + + public static final class PointIdentitySerializer implements Serializer { + @Override + public Point serialize(Point element) { + return element; + } + + @Override + public Point deserialize(Point element) { + return element; + } + } + + @SafeVarargs + public static Set asSet(E... elements) { + return new LinkedHashSet<>(Arrays.asList(elements)); + } + + public static Map asMap() { + return new LinkedHashMap<>(); + } + + public static Map asMap(K k1, V v1) { + final Map result = new LinkedHashMap<>(); + result.put(k1, v1); + return result; + } + + public static Map asMap(K k1, V v1, K k2, V v2) { + final Map result = new LinkedHashMap<>(); + result.put(k1, v1); + result.put(k2, v2); + return result; + } + + public static Map asMap(K k1, V v1, K k2, V v2, K k3, V v3) { + final Map result = new LinkedHashMap<>(); + result.put(k1, v1); + result.put(k2, v2); + result.put(k3, v3); + return result; + } + + public static Map asMap(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { + final Map result = new LinkedHashMap<>(); + result.put(k1, v1); + result.put(k2, v2); + result.put(k3, v3); + result.put(k4, v4); + return result; + } + + public static Map entriesAsMap(MEntry... entries) { + final Map result = new LinkedHashMap<>(); + Arrays.stream(entries).forEach(entry -> result.put(entry.getKey(), entry.getValue())); + + // Suppressing this warning might lead to an exception. + // Using proper generics for the MEntry class is possible. + // However, doing so increases the compilation type by several seconds + @SuppressWarnings("unchecked") + Map returnResult = (Map) result; + return returnResult; + } + + public static Map extend(Map base, MEntry... entries) { + final Map result = new LinkedHashMap<>(base); + final Map ext = entriesAsMap(entries); + result.putAll(ext); + return result; + } + + public static MEntry entry(Object key, Object val) { + return new MEntry(key, val); + } + + public static final class MEntry implements Map.Entry { + private final Object key; + private final Object val; + + public MEntry(Object key, Object val) { + this.key = key; + this.val = val; + } + + @Override + public Object getKey() { + return key; + } + + @Override + public Object getValue() { + return val; + } + + @Override + public Object setValue(Object value) { + throw new UnsupportedOperationException("setValue not supported"); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MEntry mEntry = (MEntry) o; + return Objects.equals(key, mEntry.key) && Objects.equals(val, mEntry.val); + } + + @Override + public int hashCode() { + return Objects.hash(key, val); + } + } + + + public static > boolean collectionOfArraysDeepEquals( + C collection1, + C collection2, + Supplier>> collectionFactory + ) { + Collection> c1 = collection1.stream().map(Arrays::asList) + .collect(Collectors.toCollection(collectionFactory)); + Collection> c2 = collection2.stream().map(Arrays::asList) + .collect(Collectors.toCollection(collectionFactory)); + return c1.equals(c2); + } + + public static String readFile(Path file) { + try (Stream lines = Files.lines(file)) { + return lines.collect(joining("\n")); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/YamlConfigurationStoreTest.java b/configlib-core/src/test/java/de/exlll/configlib/YamlConfigurationStoreTest.java new file mode 100644 index 0000000..4b578e6 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/YamlConfigurationStoreTest.java @@ -0,0 +1,235 @@ +package de.exlll.configlib; + +import com.google.common.jimfs.Jimfs; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.awt.Point; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static de.exlll.configlib.TestUtils.*; +import static org.junit.jupiter.api.Assertions.*; + +class YamlConfigurationStoreTest { + private final FileSystem fs = Jimfs.newFileSystem(); + private final Path yamlFile = fs.getPath("/tmp/config.yml"); + + @BeforeEach + void setUp() throws IOException { + Files.createDirectories(yamlFile.getParent()); + } + + @AfterEach + void tearDown() throws IOException { + fs.close(); + } + + @Configuration + static final class A { + String s = "S1"; + @Comment("A comment") + Integer i = null; + } + + @Test + void save() { + YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder() + .header("The\nHeader") + .footer("The\nFooter") + .outputNulls(true) + .setFieldFormatter(field -> field.getName().toUpperCase()) + .build(); + YamlConfigurationStore store = new YamlConfigurationStore<>(A.class, properties); + store.save(new A(), yamlFile); + + String expected = + """ + # The + # Header + + S: S1 + # A comment + I: null + + # The + # Footer\ + """; + assertEquals(expected, TestUtils.readFile(yamlFile)); + } + + @Configuration + static final class B { + String s = "S1"; + String t = "T1"; + Integer i = 1; + } + + @Test + void load() throws IOException { + YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder() + .inputNulls(true) + .setFieldFormatter(field -> field.getName().toUpperCase()) + .build(); + YamlConfigurationStore store = new YamlConfigurationStore<>(B.class, properties); + + Files.writeString( + yamlFile, + """ + # The + # Header + + S: S2 + t: T2 + I: null + + # The + # Footer\ + """ + ); + + B config = store.load(yamlFile); + assertEquals("S2", config.s); + assertEquals("T1", config.t); + assertNull(config.i); + } + + @Configuration + static final class C { + int i; + } + + @Test + void loadInvalidYaml() throws IOException { + YamlConfigurationStore store = newDefaultStore(C.class); + + Files.writeString( + yamlFile, + """ + - - - - - a + a + """ + ); + + assertThrowsConfigurationException( + () -> store.load(yamlFile), + "The configuration file at /tmp/config.yml does not contain valid YAML." + ); + } + + @Test + void loadEmptyYaml() throws IOException { + YamlConfigurationStore store = newDefaultStore(C.class); + + Files.writeString(yamlFile, "null"); + + assertThrowsConfigurationException( + () -> store.load(yamlFile), + "The configuration file at /tmp/config.yml is empty or only contains null." + ); + } + + @Test + void loadNonMapYaml() throws IOException { + YamlConfigurationStore store = newDefaultStore(C.class); + + Files.writeString(yamlFile, "a"); + + assertThrowsConfigurationException( + () -> store.load(yamlFile), + "The contents of the YAML file at /tmp/config.yml do not represent a " + + "configuration. A valid configuration file contains a YAML map but instead a " + + "'class java.lang.String' was found." + ); + } + + @Configuration + static final class D { + Point point = new Point(1, 2); + } + + @Test + void saveConfigurationWithInvalidTargetType() { + YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder() + .addSerializer(Point.class, TestUtils.POINT_IDENTITY_SERIALIZER) + .build(); + YamlConfigurationStore store = new YamlConfigurationStore<>(D.class, properties); + + assertThrowsConfigurationException( + () -> store.save(new D(), yamlFile), + "The given configuration could not be converted into YAML. \n" + + "Do all custom serializers produce valid target types?" + ); + } + + @Test + void saveCreatesParentDirectoriesIfPropertyTrue() { + YamlConfigurationStore store = newDefaultStore(A.class); + + Path file = fs.getPath("/a/b/c.yml"); + store.save(new A(), file); + + assertTrue(Files.exists(file.getParent())); + assertTrue(Files.exists(file)); + } + + @Test + void saveDoesNotCreateParentDirectoriesIfPropertyFalse() { + YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder() + .createParentDirectories(false) + .build(); + YamlConfigurationStore store = new YamlConfigurationStore<>(A.class, properties); + + Path file = fs.getPath("/a/b/c.yml"); + assertThrowsRuntimeException( + () -> store.save(new A(), file), + "java.nio.file.NoSuchFileException: /a/b/c.yml" + ); + } + + @Configuration + static final class E { + int i = 10; + int j = 11; + } + + @Test + void updateCreatesConfigurationFileIfItDoesNotExist() { + YamlConfigurationStore store = newDefaultStore(E.class); + + assertFalse(Files.exists(yamlFile)); + E config = store.update(yamlFile); + assertEquals("i: 10\nj: 11", readFile(yamlFile)); + assertEquals(10, config.i); + assertEquals(11, config.j); + } + + @Test + void updateLoadsConfigurationFileIfItDoesExist() throws IOException { + YamlConfigurationStore store = newDefaultStore(E.class); + + Files.writeString(yamlFile, "i: 20"); + E config = store.update(yamlFile); + assertEquals(20, config.i); + assertEquals(11, config.j); + } + + @Test + void updateUpdatesFile() throws IOException { + YamlConfigurationStore store = newDefaultStore(E.class); + + Files.writeString(yamlFile, "i: 20\nk: 30"); + E config = store.update(yamlFile); + assertEquals(20, config.i); + assertEquals(11, config.j); + assertEquals("i: 20\nj: 11", readFile(yamlFile)); + } + + private static YamlConfigurationStore newDefaultStore(Class configType) { + YamlConfigurationProperties properties = YamlConfigurationProperties.newBuilder().build(); + return new YamlConfigurationStore<>(configType, properties); + } +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/YamlFileWriterTest.java b/configlib-core/src/test/java/de/exlll/configlib/YamlFileWriterTest.java new file mode 100644 index 0000000..036bd64 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/YamlFileWriterTest.java @@ -0,0 +1,418 @@ +package de.exlll.configlib; + +import com.google.common.jimfs.Jimfs; +import de.exlll.configlib.YamlConfigurationStore.YamlFileWriter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.snakeyaml.engine.v2.api.Dump; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SuppressWarnings("unused") +class YamlFileWriterTest { + private final FileSystem fs = Jimfs.newFileSystem(); + private final Path yamlFile = fs.getPath("/tmp/config.yml"); + + @BeforeEach + void setUp() throws IOException { + Files.createDirectories(yamlFile.getParent()); + } + + @AfterEach + void tearDown() throws IOException { + fs.close(); + } + + @Configuration + static final class A { + String s = ""; + } + + @Test + void writeYamlWithNoComments() { + writeConfig(A.class); + assertFileContentEquals("s: ''"); + } + + @Test + void writeYamlWithHeaderAndFooter() { + writeConfig( + A.class, + builder -> builder + .header("This is a \n\n \nheader.") + .footer("That is a\n\n \nfooter.") + ); + assertFileContentEquals( + """ + # This is a\s + + # \s + # header. + + s: '' + + # That is a + + # \s + # footer.\ + """ + ); + } + + @Configuration + static final class B { + @Comment("Hello") + String s = "s"; + } + + @Test + void writeYamlSingleComment() { + writeConfig(B.class); + assertFileContentEquals( + """ + # Hello + s: s\ + """ + ); + } + + @Configuration + static final class C { + @Comment({"Hello", "World"}) + Map mapStringInteger = Map.of("1", 2); + @Comment({"world", "hello"}) + Map mapIntegerString = Map.of(2, "1"); + } + + @Test + void writeYamlMultipleComments() { + writeConfig(C.class); + assertFileContentEquals( + """ + # Hello + # World + mapStringInteger: + '1': 2 + # world + # hello + mapIntegerString: + 2: '1'\ + """ + ); + } + + @Configuration + static final class D { + @Comment({"Hello", "", " ", "World"}) + String s1 = "s1"; + @Comment({"", "", " ", "How are ", "you?", ""}) + String s2 = "s2"; + } + + @Test + void writeYamlEmptyComments() { + writeConfig(D.class); + assertFileContentEquals( + """ + # Hello + + # \s + # World + s1: s1 + + + # \s + # How are\s + # you? + + s2: s2\ + """ + ); + } + + @Configuration + static final class E1 { + @Comment("m") + Map> m = Map.of("c", Map.of("i", 1)); + @Comment("e2") + E2 e2 = new E2(); + } + + @Configuration + static final class E2 { + Map m = Map.of("i", 1); + @Comment("e3") + E3 e3 = new E3(); + @Comment("j") + int j = 10; + } + + @Configuration + static final class E3 { + @Comment("i") + int i = 1; + } + + @Test + void writeYamlNestedComments1() { + writeConfig(E1.class); + assertFileContentEquals( + """ + # m + m: + c: + i: 1 + # e2 + e2: + m: + i: 1 + # e3 + e3: + # i + i: 1 + # j + j: 10\ + """ + ); + } + + @Configuration + static final class F1 { + Map m1 = Map.of("i", 1); + F2 f2 = new F2(); + @Comment("f1.m2") + Map m2 = Map.of("i", 1); + } + + @Configuration + static final class F2 { + @Comment("f2.i") + int i; + } + + @Test + void writeYamlNestedComments2() { + writeConfig(F1.class); + assertFileContentEquals( + """ + m1: + i: 1 + f2: + # f2.i + i: 0 + # f1.m2 + m2: + i: 1\ + """ + ); + } + + @Configuration + static final class G1 { + @Comment("g1.g2") + G2 g2 = new G2(); + } + + @Configuration + static final class G2 { + G3 g3 = new G3(); + } + + @Configuration + static final class G3 { + G4 g4 = new G4(); + } + + @Configuration + static final class G4 { + @Comment({"g4.g3 1", "g4.g3 2"}) + int g3; + @Comment("g4.g4") + int g4; + } + + @Test + void writeYamlNestedComments3() { + writeConfig(G1.class); + assertFileContentEquals( + """ + # g1.g2 + g2: + g3: + g4: + # g4.g3 1 + # g4.g3 2 + g3: 0 + # g4.g4 + g4: 0\ + """ + ); + } + + @Configuration + static final class H1 { + @Comment("h2.1") + H2 h21 = new H2(); + @Comment("h2.2") + H2 h22 = null; + } + + @Configuration + static final class H2 { + @Comment("j") + int j = 10; + } + + @Test + void writeYamlNullFields() { + writeConfig(H1.class); + assertFileContentEquals( + """ + # h2.1 + h21: + # j + j: 10\ + """ + ); + writeConfig(H1.class, builder -> builder.outputNulls(true)); + assertFileContentEquals( + """ + # h2.1 + h21: + # j + j: 10 + # h2.2 + h22: null\ + """ + ); + } + + @Configuration + static class J1 { + @Comment("sj1") + String sJ1 = "sj1"; + } + + static final class J2 extends J1 { + @Comment("sj2") + String sJ2 = "sj2"; + } + + @Configuration + static class K1 { + @Comment("k1.j1") + J1 k1J1 = new J1(); + @Comment("k1.j2") + J2 k1J2 = new J2(); + } + + static final class K2 extends K1 { + @Comment("k2.j1") + J1 k2J1 = new J1(); + @Comment("k2.j2") + J2 k2J2 = new J2(); + } + + @Test + void writeYamlInheritance() { + writeConfig(K2.class); + assertFileContentEquals( + """ + # k1.j1 + k1J1: + # sj1 + sJ1: sj1 + # k1.j2 + k1J2: + # sj1 + sJ1: sj1 + # sj2 + sJ2: sj2 + # k2.j1 + k2J1: + # sj1 + sJ1: sj1 + # k2.j2 + k2J2: + # sj1 + sJ1: sj1 + # sj2 + sJ2: sj2\ + """ + ); + } + + @Test + void lengthCommonPrefix() { + List ab = List.of("a", "b"); + List abc = List.of("a", "b", "c"); + List abcd = List.of("a", "b", "c", "d"); + List aef = List.of("a", "e", "f"); + List def = List.of("d", "e", "f"); + + assertEquals(2, YamlFileWriter.lengthCommonPrefix(ab, ab)); + assertEquals(2, YamlFileWriter.lengthCommonPrefix(abc, ab)); + assertEquals(2, YamlFileWriter.lengthCommonPrefix(ab, abc)); + assertEquals(2, YamlFileWriter.lengthCommonPrefix(ab, abcd)); + assertEquals(3, YamlFileWriter.lengthCommonPrefix(abc, abc)); + assertEquals(3, YamlFileWriter.lengthCommonPrefix(abc, abcd)); + + assertEquals(1, YamlFileWriter.lengthCommonPrefix(ab, aef)); + assertEquals(1, YamlFileWriter.lengthCommonPrefix(abcd, aef)); + + assertEquals(0, YamlFileWriter.lengthCommonPrefix(ab, def)); + assertEquals(0, YamlFileWriter.lengthCommonPrefix(abcd, def)); + } + + String readFile() { + return TestUtils.readFile(yamlFile); + } + + record YamlFileWriterArguments( + String yaml, + Queue nodes, + YamlConfigurationProperties properties + ) {} + + void assertFileContentEquals(String expected) { + assertEquals(expected, readFile()); + } + + void writeConfig(Class cls) { + writeConfig(cls, builder -> {}); + } + + void writeConfig(Class cls, Consumer> configurer) { + YamlFileWriterArguments args = argsFromConfig(cls, Reflect.newInstance(cls), configurer); + YamlFileWriter writer = new YamlFileWriter(yamlFile, args.properties); + writer.writeYaml(args.yaml, args.nodes); + } + + static YamlFileWriterArguments argsFromConfig( + Class t, + T c, + Consumer> configurer + ) { + YamlConfigurationProperties.Builder builder = YamlConfigurationProperties.newBuilder(); + configurer.accept(builder); + YamlConfigurationProperties properties = builder.build(); + + ConfigurationSerializer serializer = new ConfigurationSerializer<>(t, properties); + Map serialize = serializer.serialize(c); + Dump dump = YamlConfigurationStore.newYamlDumper(); + String yaml = dump.dumpToString(serialize); + CommentNodeExtractor extractor = new CommentNodeExtractor(properties); + Queue nodes = extractor.extractCommentNodes(c); + return new YamlFileWriterArguments(yaml, nodes, properties); + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationA1.java b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationA1.java new file mode 100644 index 0000000..27caa98 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationA1.java @@ -0,0 +1,1418 @@ +package de.exlll.configlib.configurations; + +import de.exlll.configlib.Configuration; +import de.exlll.configlib.Ignore; + +import java.awt.Point; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal"}) +@Configuration +public class ExampleConfigurationA1 { + /* IGNORED FIELDS */ + private static final int a1_staticFinalInt = 1; + private static int a1_staticInt = 2; + private final int a1_finalInt = 3; + private transient int a1_transientInt = 4; + @Ignore + private int a1_ignoredInt = 5; + @Ignore + private String a1_ignoredString = "ignoredString"; + @Ignore + private List a1_ignoredListString = List.of("ignored", "list", "string"); + + /* PRIMITIVE TYPES */ + private boolean a1_primBool; + private char a1_primChar; + private byte a1_primByte; + private short a1_primShort; + private int a1_primInt; + private long a1_primLong; + private float a1_primFloat; + private double a1_primDouble; + + /* WRAPPER TYPES */ + private Boolean a1_refBool; + private Character a1_refChar; + private Byte a1_refByte; + private Short a1_refShort; + private Integer a1_refInt; + private Long a1_refLong; + private Float a1_refFloat; + private Double a1_refDouble; + + /* OTHER TYPES */ + private String a1_string; + private BigInteger a1_bigInteger; + private BigDecimal a1_bigDecimal; + private LocalDate a1_localDate; + private LocalTime a1_localTime; + private LocalDateTime a1_localDateTime; + private ExampleEnum a1_Enm; + + /* OTHER CONFIGURATIONS */ + private ExampleConfigurationB1 a1_b1; + private ExampleConfigurationB2 a1_b2; + + /* COLLECTIONS: Lists */ + private List a1_listBoolean; + private List a1_listChar; + private List a1_listByte; + private List a1_listShort; + private List a1_listInteger; + private List a1_listLong; + private List a1_listFloat; + private List a1_listDouble; + private List a1_listString; + private List a1_listBigInteger; + private List a1_listBigDecimal; + private List a1_listLocalDate; + private List a1_listLocalTime; + private List a1_listLocalDateTime; + private List a1_listEnm; + private List a1_listB1; + private List a1_listB2; + + /* COLLECTIONS: Arrays */ + private boolean[] a1_arrayPrimBoolean; + private char[] a1_arrayPrimChar; + private byte[] a1_arrayPrimByte; + private short[] a1_arrayPrimShort; + private int[] a1_arrayPrimInteger; + private long[] a1_arrayPrimLong; + private float[] a1_arrayPrimFloat; + private double[] a1_arrayPrimDouble; + private Boolean[] a1_arrayBoolean; + private Character[] a1_arrayChar; + private Byte[] a1_arrayByte; + private Short[] a1_arrayShort; + private Integer[] a1_arrayInteger; + private Long[] a1_arrayLong; + private Float[] a1_arrayFloat; + private Double[] a1_arrayDouble; + private String[] a1_arrayString; + private BigInteger[] a1_arrayBigInteger; + private BigDecimal[] a1_arrayBigDecimal; + private LocalDate[] a1_arrayLocalDate; + private LocalTime[] a1_arrayLocalTime; + private LocalDateTime[] a1_arrayLocalDateTime; + private ExampleEnum[] a1_arrayEnm; + private ExampleConfigurationB1[] a1_arrayB1; + private ExampleConfigurationB2[] a1_arrayB2; + + /* COLLECTIONS: Sets */ + private Set a1_setBoolean; + private Set a1_setChar; + private Set a1_setByte; + private Set a1_setShort; + private Set a1_setInteger; + private Set a1_setLong; + private Set a1_setFloat; + private Set a1_setDouble; + private Set a1_setString; + private Set a1_setBigInteger; + private Set a1_setBigDecimal; + private Set a1_setLocalDate; + private Set a1_setLocalTime; + private Set a1_setLocalDateTime; + private Set a1_setEnm; + private Set a1_setB1; + private Set a1_setB2; + + /* COLLECTIONS: Maps */ + private Map a1_mapBooleanBoolean; + private Map a1_mapCharChar; + private Map a1_mapByteByte; + private Map a1_mapShortShort; + private Map a1_mapIntegerInteger; + private Map a1_mapLongLong; + private Map a1_mapFloatFloat; + private Map a1_mapDoubleDouble; + private Map a1_mapStringString; + private Map a1_mapBigIntegerBigInteger; + private Map a1_mapBigDecimalBigDecimal; + private Map a1_mapLocalDateLocalDate; + private Map a1_mapLocalTimeLocalTime; + private Map a1_mapLocalDateTimeLocalDateTime; + private Map a1_mapEnmEnm; + + private Map a1_mapIntegerB1; + private Map a1_mapEnmB2; + + /* COLLECTIONS: Empty */ + private List a1_listEmpty; + private Integer[] a1_arrayEmpty; + private Set a1_setEmpty; + private Map a1_mapEmpty; + + /* COLLECTIONS: Nested */ + private List> a1_listListByte; + private List a1_listArrayFloat; + private List> a1_listSetString; + private List> a1_listMapEnmLocalDate; + + private Set> a1_setSetShort; + private Set a1_setArrayDouble; + private Set> a1_setListString; + private Set> a1_setMapEnmLocalTime; + + private Map> a1_mapIntegerMapLongBoolean; + private Map> a1_mapStringListB1; + private Map a1_mapBigIntegerArrayBigDecimal; + private Map> a1_mapEnmSetB2; + + private Map>>> + a1_mapIntegerListMapShortSetB2; + + private boolean[][] a1_arrayArrayPrimBoolean; + private char[][] a1_arrayArrayPrimChar; + private byte[][] a1_arrayArrayPrimByte; + private short[][] a1_arrayArrayPrimShort; + private int[][] a1_arrayArrayPrimInteger; + private long[][] a1_arrayArrayPrimLong; + private float[][] a1_arrayArrayPrimFloat; + private double[][] a1_arrayArrayPrimDouble; + private Boolean[][] a1_arrayArrayBoolean; + private Character[][] a1_arrayArrayChar; + private Byte[][] a1_arrayArrayByte; + private Short[][] a1_arrayArrayShort; + private Integer[][] a1_arrayArrayInteger; + private Long[][] a1_arrayArrayLong; + private Float[][] a1_arrayArrayFloat; + private Double[][] a1_arrayArrayDouble; + private String[][] a1_arrayArrayString; + private BigInteger[][] a1_arrayArrayBigInteger; + private BigDecimal[][] a1_arrayArrayBigDecimal; + private LocalDate[][] a1_arrayArrayLocalDate; + private LocalTime[][] a1_arrayArrayLocalTime; + private LocalDateTime[][] a1_arrayArrayLocalDateTime; + private ExampleEnum[][] a1_arrayArrayEnm; + private ExampleConfigurationB1[][] a1_arrayArrayB1; + private ExampleConfigurationB2[][] a1_arrayArrayB2; + + /* CUSTOM CONVERTERS */ + private Point a1_point; + private List a1_listPoint; + private Point[] a1_arrayPoint; + private Set a1_setPoint; + private Map> a1_mapEnmListPoint; + + public static int getA1_staticFinalInt() { + return a1_staticFinalInt; + } + + public static int getA1_staticInt() { + return a1_staticInt; + } + + public int getA1_finalInt() { + return a1_finalInt; + } + + public int getA1_transientInt() { + return a1_transientInt; + } + + public int getA1_ignoredInt() { + return a1_ignoredInt; + } + + public String getA1_ignoredString() { + return a1_ignoredString; + } + + public List getA1_ignoredListString() { + return a1_ignoredListString; + } + + public boolean isA1_primBool() { + return a1_primBool; + } + + public void setA1_primBool(boolean a1_primBool) { + this.a1_primBool = a1_primBool; + } + + public char getA1_primChar() { + return a1_primChar; + } + + public void setA1_primChar(char a1_primChar) { + this.a1_primChar = a1_primChar; + } + + public byte getA1_primByte() { + return a1_primByte; + } + + public void setA1_primByte(byte a1_primByte) { + this.a1_primByte = a1_primByte; + } + + public short getA1_primShort() { + return a1_primShort; + } + + public void setA1_primShort(short a1_primShort) { + this.a1_primShort = a1_primShort; + } + + public int getA1_primInt() { + return a1_primInt; + } + + public void setA1_primInt(int a1_primInt) { + this.a1_primInt = a1_primInt; + } + + public long getA1_primLong() { + return a1_primLong; + } + + public void setA1_primLong(long a1_primLong) { + this.a1_primLong = a1_primLong; + } + + public float getA1_primFloat() { + return a1_primFloat; + } + + public void setA1_primFloat(float a1_primFloat) { + this.a1_primFloat = a1_primFloat; + } + + public double getA1_primDouble() { + return a1_primDouble; + } + + public void setA1_primDouble(double a1_primDouble) { + this.a1_primDouble = a1_primDouble; + } + + public Boolean getA1_refBool() { + return a1_refBool; + } + + public void setA1_refBool(Boolean a1_refBool) { + this.a1_refBool = a1_refBool; + } + + public Character getA1_refChar() { + return a1_refChar; + } + + public void setA1_refChar(Character a1_refChar) { + this.a1_refChar = a1_refChar; + } + + public Byte getA1_refByte() { + return a1_refByte; + } + + public void setA1_refByte(Byte a1_refByte) { + this.a1_refByte = a1_refByte; + } + + public Short getA1_refShort() { + return a1_refShort; + } + + public void setA1_refShort(Short a1_refShort) { + this.a1_refShort = a1_refShort; + } + + public Integer getA1_refInt() { + return a1_refInt; + } + + public void setA1_refInt(Integer a1_refInt) { + this.a1_refInt = a1_refInt; + } + + public Long getA1_refLong() { + return a1_refLong; + } + + public void setA1_refLong(Long a1_refLong) { + this.a1_refLong = a1_refLong; + } + + public Float getA1_refFloat() { + return a1_refFloat; + } + + public void setA1_refFloat(Float a1_refFloat) { + this.a1_refFloat = a1_refFloat; + } + + public Double getA1_refDouble() { + return a1_refDouble; + } + + public void setA1_refDouble(Double a1_refDouble) { + this.a1_refDouble = a1_refDouble; + } + + public String getA1_string() { + return a1_string; + } + + public void setA1_string(String a1_string) { + this.a1_string = a1_string; + } + + public BigInteger getA1_bigInteger() { + return a1_bigInteger; + } + + public void setA1_bigInteger(BigInteger a1_bigInteger) { + this.a1_bigInteger = a1_bigInteger; + } + + public BigDecimal getA1_bigDecimal() { + return a1_bigDecimal; + } + + public void setA1_bigDecimal(BigDecimal a1_bigDecimal) { + this.a1_bigDecimal = a1_bigDecimal; + } + + public LocalDate getA1_localDate() { + return a1_localDate; + } + + public void setA1_localDate(LocalDate a1_localDate) { + this.a1_localDate = a1_localDate; + } + + public LocalTime getA1_localTime() { + return a1_localTime; + } + + public void setA1_localTime(LocalTime a1_localTime) { + this.a1_localTime = a1_localTime; + } + + public LocalDateTime getA1_localDateTime() { + return a1_localDateTime; + } + + public void setA1_localDateTime(LocalDateTime a1_localDateTime) { + this.a1_localDateTime = a1_localDateTime; + } + + public ExampleEnum getA1_Enm() { + return a1_Enm; + } + + public void setA1_Enm(ExampleEnum a1_Enm) { + this.a1_Enm = a1_Enm; + } + + public ExampleConfigurationB1 getA1_b1() { + return a1_b1; + } + + public void setA1_b1(ExampleConfigurationB1 a1_b1) { + this.a1_b1 = a1_b1; + } + + public ExampleConfigurationB2 getA1_b2() { + return a1_b2; + } + + public void setA1_b2(ExampleConfigurationB2 a1_b2) { + this.a1_b2 = a1_b2; + } + + public List getA1_listBoolean() { + return a1_listBoolean; + } + + public void setA1_listBoolean(List a1_listBoolean) { + this.a1_listBoolean = a1_listBoolean; + } + + public List getA1_listChar() { + return a1_listChar; + } + + public void setA1_listChar(List a1_listChar) { + this.a1_listChar = a1_listChar; + } + + public List getA1_listByte() { + return a1_listByte; + } + + public void setA1_listByte(List a1_listByte) { + this.a1_listByte = a1_listByte; + } + + public List getA1_listShort() { + return a1_listShort; + } + + public void setA1_listShort(List a1_listShort) { + this.a1_listShort = a1_listShort; + } + + public List getA1_listInteger() { + return a1_listInteger; + } + + public void setA1_listInteger(List a1_listInteger) { + this.a1_listInteger = a1_listInteger; + } + + public List getA1_listLong() { + return a1_listLong; + } + + public void setA1_listLong(List a1_listLong) { + this.a1_listLong = a1_listLong; + } + + public List getA1_listFloat() { + return a1_listFloat; + } + + public void setA1_listFloat(List a1_listFloat) { + this.a1_listFloat = a1_listFloat; + } + + public List getA1_listDouble() { + return a1_listDouble; + } + + public void setA1_listDouble(List a1_listDouble) { + this.a1_listDouble = a1_listDouble; + } + + public List getA1_listString() { + return a1_listString; + } + + public void setA1_listString(List a1_listString) { + this.a1_listString = a1_listString; + } + + public List getA1_listBigInteger() { + return a1_listBigInteger; + } + + public void setA1_listBigInteger(List a1_listBigInteger) { + this.a1_listBigInteger = a1_listBigInteger; + } + + public List getA1_listBigDecimal() { + return a1_listBigDecimal; + } + + public void setA1_listBigDecimal(List a1_listBigDecimal) { + this.a1_listBigDecimal = a1_listBigDecimal; + } + + public List getA1_listLocalDate() { + return a1_listLocalDate; + } + + public void setA1_listLocalDate(List a1_listLocalDate) { + this.a1_listLocalDate = a1_listLocalDate; + } + + public List getA1_listLocalTime() { + return a1_listLocalTime; + } + + public void setA1_listLocalTime(List a1_listLocalTime) { + this.a1_listLocalTime = a1_listLocalTime; + } + + public List getA1_listLocalDateTime() { + return a1_listLocalDateTime; + } + + public void setA1_listLocalDateTime(List a1_listLocalDateTime) { + this.a1_listLocalDateTime = a1_listLocalDateTime; + } + + public List getA1_listEnm() { + return a1_listEnm; + } + + public void setA1_listEnm(List a1_listEnm) { + this.a1_listEnm = a1_listEnm; + } + + public List getA1_listB1() { + return a1_listB1; + } + + public void setA1_listB1(List a1_listB1) { + this.a1_listB1 = a1_listB1; + } + + public List getA1_listB2() { + return a1_listB2; + } + + public void setA1_listB2(List a1_listB2) { + this.a1_listB2 = a1_listB2; + } + + public boolean[] getA1_arrayPrimBoolean() { + return a1_arrayPrimBoolean; + } + + public void setA1_arrayPrimBoolean(boolean[] a1_arrayPrimBoolean) { + this.a1_arrayPrimBoolean = a1_arrayPrimBoolean; + } + + public char[] getA1_arrayPrimChar() { + return a1_arrayPrimChar; + } + + public void setA1_arrayPrimChar(char[] a1_arrayPrimChar) { + this.a1_arrayPrimChar = a1_arrayPrimChar; + } + + public byte[] getA1_arrayPrimByte() { + return a1_arrayPrimByte; + } + + public void setA1_arrayPrimByte(byte[] a1_arrayPrimByte) { + this.a1_arrayPrimByte = a1_arrayPrimByte; + } + + public short[] getA1_arrayPrimShort() { + return a1_arrayPrimShort; + } + + public void setA1_arrayPrimShort(short[] a1_arrayPrimShort) { + this.a1_arrayPrimShort = a1_arrayPrimShort; + } + + public int[] getA1_arrayPrimInteger() { + return a1_arrayPrimInteger; + } + + public void setA1_arrayPrimInteger(int[] a1_arrayPrimInteger) { + this.a1_arrayPrimInteger = a1_arrayPrimInteger; + } + + public long[] getA1_arrayPrimLong() { + return a1_arrayPrimLong; + } + + public void setA1_arrayPrimLong(long[] a1_arrayPrimLong) { + this.a1_arrayPrimLong = a1_arrayPrimLong; + } + + public float[] getA1_arrayPrimFloat() { + return a1_arrayPrimFloat; + } + + public void setA1_arrayPrimFloat(float[] a1_arrayPrimFloat) { + this.a1_arrayPrimFloat = a1_arrayPrimFloat; + } + + public double[] getA1_arrayPrimDouble() { + return a1_arrayPrimDouble; + } + + public void setA1_arrayPrimDouble(double[] a1_arrayPrimDouble) { + this.a1_arrayPrimDouble = a1_arrayPrimDouble; + } + + public Boolean[] getA1_arrayBoolean() { + return a1_arrayBoolean; + } + + public void setA1_arrayBoolean(Boolean[] a1_arrayBoolean) { + this.a1_arrayBoolean = a1_arrayBoolean; + } + + public Character[] getA1_arrayChar() { + return a1_arrayChar; + } + + public void setA1_arrayChar(Character[] a1_arrayChar) { + this.a1_arrayChar = a1_arrayChar; + } + + public Byte[] getA1_arrayByte() { + return a1_arrayByte; + } + + public void setA1_arrayByte(Byte[] a1_arrayByte) { + this.a1_arrayByte = a1_arrayByte; + } + + public Short[] getA1_arrayShort() { + return a1_arrayShort; + } + + public void setA1_arrayShort(Short[] a1_arrayShort) { + this.a1_arrayShort = a1_arrayShort; + } + + public Integer[] getA1_arrayInteger() { + return a1_arrayInteger; + } + + public void setA1_arrayInteger(Integer[] a1_arrayInteger) { + this.a1_arrayInteger = a1_arrayInteger; + } + + public Long[] getA1_arrayLong() { + return a1_arrayLong; + } + + public void setA1_arrayLong(Long[] a1_arrayLong) { + this.a1_arrayLong = a1_arrayLong; + } + + public Float[] getA1_arrayFloat() { + return a1_arrayFloat; + } + + public void setA1_arrayFloat(Float[] a1_arrayFloat) { + this.a1_arrayFloat = a1_arrayFloat; + } + + public Double[] getA1_arrayDouble() { + return a1_arrayDouble; + } + + public void setA1_arrayDouble(Double[] a1_arrayDouble) { + this.a1_arrayDouble = a1_arrayDouble; + } + + public String[] getA1_arrayString() { + return a1_arrayString; + } + + public void setA1_arrayString(String[] a1_arrayString) { + this.a1_arrayString = a1_arrayString; + } + + public BigInteger[] getA1_arrayBigInteger() { + return a1_arrayBigInteger; + } + + public void setA1_arrayBigInteger(BigInteger[] a1_arrayBigInteger) { + this.a1_arrayBigInteger = a1_arrayBigInteger; + } + + public BigDecimal[] getA1_arrayBigDecimal() { + return a1_arrayBigDecimal; + } + + public void setA1_arrayBigDecimal(BigDecimal[] a1_arrayBigDecimal) { + this.a1_arrayBigDecimal = a1_arrayBigDecimal; + } + + public LocalDate[] getA1_arrayLocalDate() { + return a1_arrayLocalDate; + } + + public void setA1_arrayLocalDate(LocalDate[] a1_arrayLocalDate) { + this.a1_arrayLocalDate = a1_arrayLocalDate; + } + + public LocalTime[] getA1_arrayLocalTime() { + return a1_arrayLocalTime; + } + + public void setA1_arrayLocalTime(LocalTime[] a1_arrayLocalTime) { + this.a1_arrayLocalTime = a1_arrayLocalTime; + } + + public LocalDateTime[] getA1_arrayLocalDateTime() { + return a1_arrayLocalDateTime; + } + + public void setA1_arrayLocalDateTime(LocalDateTime[] a1_arrayLocalDateTime) { + this.a1_arrayLocalDateTime = a1_arrayLocalDateTime; + } + + public ExampleEnum[] getA1_arrayEnm() { + return a1_arrayEnm; + } + + public void setA1_arrayEnm(ExampleEnum[] a1_arrayEnm) { + this.a1_arrayEnm = a1_arrayEnm; + } + + public ExampleConfigurationB1[] getA1_arrayB1() { + return a1_arrayB1; + } + + public void setA1_arrayB1(ExampleConfigurationB1[] a1_arrayB1) { + this.a1_arrayB1 = a1_arrayB1; + } + + public ExampleConfigurationB2[] getA1_arrayB2() { + return a1_arrayB2; + } + + public void setA1_arrayB2(ExampleConfigurationB2[] a1_arrayB2) { + this.a1_arrayB2 = a1_arrayB2; + } + + public Set getA1_setBoolean() { + return a1_setBoolean; + } + + public void setA1_setBoolean(Set a1_setBoolean) { + this.a1_setBoolean = a1_setBoolean; + } + + public Set getA1_setChar() { + return a1_setChar; + } + + public void setA1_setChar(Set a1_setChar) { + this.a1_setChar = a1_setChar; + } + + public Set getA1_setByte() { + return a1_setByte; + } + + public void setA1_setByte(Set a1_setByte) { + this.a1_setByte = a1_setByte; + } + + public Set getA1_setShort() { + return a1_setShort; + } + + public void setA1_setShort(Set a1_setShort) { + this.a1_setShort = a1_setShort; + } + + public Set getA1_setInteger() { + return a1_setInteger; + } + + public void setA1_setInteger(Set a1_setInteger) { + this.a1_setInteger = a1_setInteger; + } + + public Set getA1_setLong() { + return a1_setLong; + } + + public void setA1_setLong(Set a1_setLong) { + this.a1_setLong = a1_setLong; + } + + public Set getA1_setFloat() { + return a1_setFloat; + } + + public void setA1_setFloat(Set a1_setFloat) { + this.a1_setFloat = a1_setFloat; + } + + public Set getA1_setDouble() { + return a1_setDouble; + } + + public void setA1_setDouble(Set a1_setDouble) { + this.a1_setDouble = a1_setDouble; + } + + public Set getA1_setString() { + return a1_setString; + } + + public void setA1_setString(Set a1_setString) { + this.a1_setString = a1_setString; + } + + public Set getA1_setBigInteger() { + return a1_setBigInteger; + } + + public void setA1_setBigInteger(Set a1_setBigInteger) { + this.a1_setBigInteger = a1_setBigInteger; + } + + public Set getA1_setBigDecimal() { + return a1_setBigDecimal; + } + + public void setA1_setBigDecimal(Set a1_setBigDecimal) { + this.a1_setBigDecimal = a1_setBigDecimal; + } + + public Set getA1_setLocalDate() { + return a1_setLocalDate; + } + + public void setA1_setLocalDate(Set a1_setLocalDate) { + this.a1_setLocalDate = a1_setLocalDate; + } + + public Set getA1_setLocalTime() { + return a1_setLocalTime; + } + + public void setA1_setLocalTime(Set a1_setLocalTime) { + this.a1_setLocalTime = a1_setLocalTime; + } + + public Set getA1_setLocalDateTime() { + return a1_setLocalDateTime; + } + + public void setA1_setLocalDateTime(Set a1_setLocalDateTime) { + this.a1_setLocalDateTime = a1_setLocalDateTime; + } + + public Set getA1_setEnm() { + return a1_setEnm; + } + + public void setA1_setEnm(Set a1_setEnm) { + this.a1_setEnm = a1_setEnm; + } + + public Set getA1_setB1() { + return a1_setB1; + } + + public void setA1_setB1(Set a1_setB1) { + this.a1_setB1 = a1_setB1; + } + + public Set getA1_setB2() { + return a1_setB2; + } + + public void setA1_setB2(Set a1_setB2) { + this.a1_setB2 = a1_setB2; + } + + public Map getA1_mapBooleanBoolean() { + return a1_mapBooleanBoolean; + } + + public void setA1_mapBooleanBoolean(Map a1_mapBooleanBoolean) { + this.a1_mapBooleanBoolean = a1_mapBooleanBoolean; + } + + public Map getA1_mapCharChar() { + return a1_mapCharChar; + } + + public void setA1_mapCharChar(Map a1_mapCharChar) { + this.a1_mapCharChar = a1_mapCharChar; + } + + public Map getA1_mapByteByte() { + return a1_mapByteByte; + } + + public void setA1_mapByteByte(Map a1_mapByteByte) { + this.a1_mapByteByte = a1_mapByteByte; + } + + public Map getA1_mapShortShort() { + return a1_mapShortShort; + } + + public void setA1_mapShortShort(Map a1_mapShortShort) { + this.a1_mapShortShort = a1_mapShortShort; + } + + public Map getA1_mapIntegerInteger() { + return a1_mapIntegerInteger; + } + + public void setA1_mapIntegerInteger(Map a1_mapIntegerInteger) { + this.a1_mapIntegerInteger = a1_mapIntegerInteger; + } + + public Map getA1_mapLongLong() { + return a1_mapLongLong; + } + + public void setA1_mapLongLong(Map a1_mapLongLong) { + this.a1_mapLongLong = a1_mapLongLong; + } + + public Map getA1_mapFloatFloat() { + return a1_mapFloatFloat; + } + + public void setA1_mapFloatFloat(Map a1_mapFloatFloat) { + this.a1_mapFloatFloat = a1_mapFloatFloat; + } + + public Map getA1_mapDoubleDouble() { + return a1_mapDoubleDouble; + } + + public void setA1_mapDoubleDouble(Map a1_mapDoubleDouble) { + this.a1_mapDoubleDouble = a1_mapDoubleDouble; + } + + public Map getA1_mapStringString() { + return a1_mapStringString; + } + + public void setA1_mapStringString(Map a1_mapStringString) { + this.a1_mapStringString = a1_mapStringString; + } + + public Map getA1_mapBigIntegerBigInteger() { + return a1_mapBigIntegerBigInteger; + } + + public void setA1_mapBigIntegerBigInteger(Map a1_mapBigIntegerBigInteger) { + this.a1_mapBigIntegerBigInteger = a1_mapBigIntegerBigInteger; + } + + public Map getA1_mapBigDecimalBigDecimal() { + return a1_mapBigDecimalBigDecimal; + } + + public void setA1_mapBigDecimalBigDecimal(Map a1_mapBigDecimalBigDecimal) { + this.a1_mapBigDecimalBigDecimal = a1_mapBigDecimalBigDecimal; + } + + public Map getA1_mapLocalDateLocalDate() { + return a1_mapLocalDateLocalDate; + } + + public void setA1_mapLocalDateLocalDate(Map a1_mapLocalDateLocalDate) { + this.a1_mapLocalDateLocalDate = a1_mapLocalDateLocalDate; + } + + public Map getA1_mapLocalTimeLocalTime() { + return a1_mapLocalTimeLocalTime; + } + + public void setA1_mapLocalTimeLocalTime(Map a1_mapLocalTimeLocalTime) { + this.a1_mapLocalTimeLocalTime = a1_mapLocalTimeLocalTime; + } + + public Map getA1_mapLocalDateTimeLocalDateTime() { + return a1_mapLocalDateTimeLocalDateTime; + } + + public void setA1_mapLocalDateTimeLocalDateTime(Map a1_mapLocalDateTimeLocalDateTime) { + this.a1_mapLocalDateTimeLocalDateTime = a1_mapLocalDateTimeLocalDateTime; + } + + public Map getA1_mapEnmEnm() { + return a1_mapEnmEnm; + } + + public void setA1_mapEnmEnm(Map a1_mapEnmEnm) { + this.a1_mapEnmEnm = a1_mapEnmEnm; + } + + public Map getA1_mapIntegerB1() { + return a1_mapIntegerB1; + } + + public void setA1_mapIntegerB1(Map a1_mapIntegerB1) { + this.a1_mapIntegerB1 = a1_mapIntegerB1; + } + + public Map getA1_mapEnmB2() { + return a1_mapEnmB2; + } + + public void setA1_mapEnmB2(Map a1_mapEnmB2) { + this.a1_mapEnmB2 = a1_mapEnmB2; + } + + public List getA1_listEmpty() { + return a1_listEmpty; + } + + public void setA1_listEmpty(List a1_listEmpty) { + this.a1_listEmpty = a1_listEmpty; + } + + public Integer[] getA1_arrayEmpty() { + return a1_arrayEmpty; + } + + public void setA1_arrayEmpty(Integer[] a1_arrayEmpty) { + this.a1_arrayEmpty = a1_arrayEmpty; + } + + public Set getA1_setEmpty() { + return a1_setEmpty; + } + + public void setA1_setEmpty(Set a1_setEmpty) { + this.a1_setEmpty = a1_setEmpty; + } + + public Map getA1_mapEmpty() { + return a1_mapEmpty; + } + + public void setA1_mapEmpty(Map a1_mapEmpty) { + this.a1_mapEmpty = a1_mapEmpty; + } + + public List> getA1_listListByte() { + return a1_listListByte; + } + + public void setA1_listListByte(List> a1_listListByte) { + this.a1_listListByte = a1_listListByte; + } + + public List getA1_listArrayFloat() { + return a1_listArrayFloat; + } + + public void setA1_listArrayFloat(List a1_listArrayFloat) { + this.a1_listArrayFloat = a1_listArrayFloat; + } + + public List> getA1_listSetString() { + return a1_listSetString; + } + + public void setA1_listSetString(List> a1_listSetString) { + this.a1_listSetString = a1_listSetString; + } + + public List> getA1_listMapEnmLocalDate() { + return a1_listMapEnmLocalDate; + } + + public void setA1_listMapEnmLocalDate(List> a1_listMapEnmLocalDate) { + this.a1_listMapEnmLocalDate = a1_listMapEnmLocalDate; + } + + public Set> getA1_setSetShort() { + return a1_setSetShort; + } + + public void setA1_setSetShort(Set> a1_setSetShort) { + this.a1_setSetShort = a1_setSetShort; + } + + public Set getA1_setArrayDouble() { + return a1_setArrayDouble; + } + + public void setA1_setArrayDouble(Set a1_setArrayDouble) { + this.a1_setArrayDouble = a1_setArrayDouble; + } + + public Set> getA1_setListString() { + return a1_setListString; + } + + public void setA1_setListString(Set> a1_setListString) { + this.a1_setListString = a1_setListString; + } + + public Set> getA1_setMapEnmLocalTime() { + return a1_setMapEnmLocalTime; + } + + public void setA1_setMapEnmLocalTime(Set> a1_setMapEnmLocalTime) { + this.a1_setMapEnmLocalTime = a1_setMapEnmLocalTime; + } + + public Map> getA1_mapIntegerMapLongBoolean() { + return a1_mapIntegerMapLongBoolean; + } + + public void setA1_mapIntegerMapLongBoolean(Map> a1_mapIntegerMapLongBoolean) { + this.a1_mapIntegerMapLongBoolean = a1_mapIntegerMapLongBoolean; + } + + public Map> getA1_mapStringListB1() { + return a1_mapStringListB1; + } + + public void setA1_mapStringListB1(Map> a1_mapStringListB1) { + this.a1_mapStringListB1 = a1_mapStringListB1; + } + + public Map getA1_mapBigIntegerArrayBigDecimal() { + return a1_mapBigIntegerArrayBigDecimal; + } + + public void setA1_mapBigIntegerArrayBigDecimal(Map a1_mapBigIntegerArrayBigDecimal) { + this.a1_mapBigIntegerArrayBigDecimal = a1_mapBigIntegerArrayBigDecimal; + } + + public Map> getA1_mapEnmSetB2() { + return a1_mapEnmSetB2; + } + + public void setA1_mapEnmSetB2(Map> a1_mapEnmSetB2) { + this.a1_mapEnmSetB2 = a1_mapEnmSetB2; + } + + public Map>>> getA1_mapIntegerListMapShortSetB2() { + return a1_mapIntegerListMapShortSetB2; + } + + public void setA1_mapIntegerListMapShortSetB2(Map>>> a1_mapIntegerListMapShortSetB2) { + this.a1_mapIntegerListMapShortSetB2 = a1_mapIntegerListMapShortSetB2; + } + + public boolean[][] getA1_arrayArrayPrimBoolean() { + return a1_arrayArrayPrimBoolean; + } + + public void setA1_arrayArrayPrimBoolean(boolean[][] a1_arrayArrayPrimBoolean) { + this.a1_arrayArrayPrimBoolean = a1_arrayArrayPrimBoolean; + } + + public char[][] getA1_arrayArrayPrimChar() { + return a1_arrayArrayPrimChar; + } + + public void setA1_arrayArrayPrimChar(char[][] a1_arrayArrayPrimChar) { + this.a1_arrayArrayPrimChar = a1_arrayArrayPrimChar; + } + + public byte[][] getA1_arrayArrayPrimByte() { + return a1_arrayArrayPrimByte; + } + + public void setA1_arrayArrayPrimByte(byte[][] a1_arrayArrayPrimByte) { + this.a1_arrayArrayPrimByte = a1_arrayArrayPrimByte; + } + + public short[][] getA1_arrayArrayPrimShort() { + return a1_arrayArrayPrimShort; + } + + public void setA1_arrayArrayPrimShort(short[][] a1_arrayArrayPrimShort) { + this.a1_arrayArrayPrimShort = a1_arrayArrayPrimShort; + } + + public int[][] getA1_arrayArrayPrimInteger() { + return a1_arrayArrayPrimInteger; + } + + public void setA1_arrayArrayPrimInteger(int[][] a1_arrayArrayPrimInteger) { + this.a1_arrayArrayPrimInteger = a1_arrayArrayPrimInteger; + } + + public long[][] getA1_arrayArrayPrimLong() { + return a1_arrayArrayPrimLong; + } + + public void setA1_arrayArrayPrimLong(long[][] a1_arrayArrayPrimLong) { + this.a1_arrayArrayPrimLong = a1_arrayArrayPrimLong; + } + + public float[][] getA1_arrayArrayPrimFloat() { + return a1_arrayArrayPrimFloat; + } + + public void setA1_arrayArrayPrimFloat(float[][] a1_arrayArrayPrimFloat) { + this.a1_arrayArrayPrimFloat = a1_arrayArrayPrimFloat; + } + + public double[][] getA1_arrayArrayPrimDouble() { + return a1_arrayArrayPrimDouble; + } + + public void setA1_arrayArrayPrimDouble(double[][] a1_arrayArrayPrimDouble) { + this.a1_arrayArrayPrimDouble = a1_arrayArrayPrimDouble; + } + + public Boolean[][] getA1_arrayArrayBoolean() { + return a1_arrayArrayBoolean; + } + + public void setA1_arrayArrayBoolean(Boolean[][] a1_arrayArrayBoolean) { + this.a1_arrayArrayBoolean = a1_arrayArrayBoolean; + } + + public Character[][] getA1_arrayArrayChar() { + return a1_arrayArrayChar; + } + + public void setA1_arrayArrayChar(Character[][] a1_arrayArrayChar) { + this.a1_arrayArrayChar = a1_arrayArrayChar; + } + + public Byte[][] getA1_arrayArrayByte() { + return a1_arrayArrayByte; + } + + public void setA1_arrayArrayByte(Byte[][] a1_arrayArrayByte) { + this.a1_arrayArrayByte = a1_arrayArrayByte; + } + + public Short[][] getA1_arrayArrayShort() { + return a1_arrayArrayShort; + } + + public void setA1_arrayArrayShort(Short[][] a1_arrayArrayShort) { + this.a1_arrayArrayShort = a1_arrayArrayShort; + } + + public Integer[][] getA1_arrayArrayInteger() { + return a1_arrayArrayInteger; + } + + public void setA1_arrayArrayInteger(Integer[][] a1_arrayArrayInteger) { + this.a1_arrayArrayInteger = a1_arrayArrayInteger; + } + + public Long[][] getA1_arrayArrayLong() { + return a1_arrayArrayLong; + } + + public void setA1_arrayArrayLong(Long[][] a1_arrayArrayLong) { + this.a1_arrayArrayLong = a1_arrayArrayLong; + } + + public Float[][] getA1_arrayArrayFloat() { + return a1_arrayArrayFloat; + } + + public void setA1_arrayArrayFloat(Float[][] a1_arrayArrayFloat) { + this.a1_arrayArrayFloat = a1_arrayArrayFloat; + } + + public Double[][] getA1_arrayArrayDouble() { + return a1_arrayArrayDouble; + } + + public void setA1_arrayArrayDouble(Double[][] a1_arrayArrayDouble) { + this.a1_arrayArrayDouble = a1_arrayArrayDouble; + } + + public String[][] getA1_arrayArrayString() { + return a1_arrayArrayString; + } + + public void setA1_arrayArrayString(String[][] a1_arrayArrayString) { + this.a1_arrayArrayString = a1_arrayArrayString; + } + + public BigInteger[][] getA1_arrayArrayBigInteger() { + return a1_arrayArrayBigInteger; + } + + public void setA1_arrayArrayBigInteger(BigInteger[][] a1_arrayArrayBigInteger) { + this.a1_arrayArrayBigInteger = a1_arrayArrayBigInteger; + } + + public BigDecimal[][] getA1_arrayArrayBigDecimal() { + return a1_arrayArrayBigDecimal; + } + + public void setA1_arrayArrayBigDecimal(BigDecimal[][] a1_arrayArrayBigDecimal) { + this.a1_arrayArrayBigDecimal = a1_arrayArrayBigDecimal; + } + + public LocalDate[][] getA1_arrayArrayLocalDate() { + return a1_arrayArrayLocalDate; + } + + public void setA1_arrayArrayLocalDate(LocalDate[][] a1_arrayArrayLocalDate) { + this.a1_arrayArrayLocalDate = a1_arrayArrayLocalDate; + } + + public LocalTime[][] getA1_arrayArrayLocalTime() { + return a1_arrayArrayLocalTime; + } + + public void setA1_arrayArrayLocalTime(LocalTime[][] a1_arrayArrayLocalTime) { + this.a1_arrayArrayLocalTime = a1_arrayArrayLocalTime; + } + + public LocalDateTime[][] getA1_arrayArrayLocalDateTime() { + return a1_arrayArrayLocalDateTime; + } + + public void setA1_arrayArrayLocalDateTime(LocalDateTime[][] a1_arrayArrayLocalDateTime) { + this.a1_arrayArrayLocalDateTime = a1_arrayArrayLocalDateTime; + } + + public ExampleEnum[][] getA1_arrayArrayEnm() { + return a1_arrayArrayEnm; + } + + public void setA1_arrayArrayEnm(ExampleEnum[][] a1_arrayArrayEnm) { + this.a1_arrayArrayEnm = a1_arrayArrayEnm; + } + + public ExampleConfigurationB1[][] getA1_arrayArrayB1() { + return a1_arrayArrayB1; + } + + public void setA1_arrayArrayB1(ExampleConfigurationB1[][] a1_arrayArrayB1) { + this.a1_arrayArrayB1 = a1_arrayArrayB1; + } + + public ExampleConfigurationB2[][] getA1_arrayArrayB2() { + return a1_arrayArrayB2; + } + + public void setA1_arrayArrayB2(ExampleConfigurationB2[][] a1_arrayArrayB2) { + this.a1_arrayArrayB2 = a1_arrayArrayB2; + } + + public Point getA1_point() { + return a1_point; + } + + public void setA1_point(Point a1_point) { + this.a1_point = a1_point; + } + + public List getA1_listPoint() { + return a1_listPoint; + } + + public void setA1_listPoint(List a1_listPoint) { + this.a1_listPoint = a1_listPoint; + } + + public Point[] getA1_arrayPoint() { + return a1_arrayPoint; + } + + public void setA1_arrayPoint(Point[] a1_arrayPoint) { + this.a1_arrayPoint = a1_arrayPoint; + } + + public Set getA1_setPoint() { + return a1_setPoint; + } + + public void setA1_setPoint(Set a1_setPoint) { + this.a1_setPoint = a1_setPoint; + } + + public Map> getA1_mapEnmListPoint() { + return a1_mapEnmListPoint; + } + + public void setA1_mapEnmListPoint(Map> a1_mapEnmListPoint) { + this.a1_mapEnmListPoint = a1_mapEnmListPoint; + } +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationA2.java b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationA2.java new file mode 100644 index 0000000..86db4c4 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationA2.java @@ -0,0 +1,1450 @@ +package de.exlll.configlib.configurations; + +import de.exlll.configlib.Ignore; + +import java.awt.Point; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal"}) +public final class ExampleConfigurationA2 extends ExampleConfigurationA1 { + /* IGNORED FIELDS */ + private static final int a2_staticFinalInt = 1; + private static int a2_staticInt = 2; + private final int a2_finalInt = 3; + private transient int a2_transientInt = 4; + @Ignore + private int a2_ignoredInt = 5; + @Ignore + private String a2_ignoredString = "ignoredString"; + @Ignore + private List a2_ignoredListString = List.of("ignored", "list", "string"); + + /* PRIMITIVE TYPES */ + private boolean a2_primBool; + private char a2_primChar; + private byte a2_primByte; + private short a2_primShort; + private int a2_primInt; + private long a2_primLong; + private float a2_primFloat; + private double a2_primDouble; + + /* WRAPPER TYPES */ + private Boolean a2_refBool; + private Character a2_refChar; + private Byte a2_refByte; + private Short a2_refShort; + private Integer a2_refInt; + private Long a2_refLong; + private Float a2_refFloat; + private Double a2_refDouble; + + /* OTHER TYPES */ + private String a2_string; + private BigInteger a2_bigInteger; + private BigDecimal a2_bigDecimal; + private LocalDate a2_localDate; + private LocalTime a2_localTime; + private LocalDateTime a2_localDateTime; + private ExampleEnum a2_Enm; + + /* OTHER CONFIGURATIONS */ + private ExampleConfigurationB1 a2_b1; + private ExampleConfigurationB2 a2_b2; + + /* COLLECTIONS: Lists */ + private List a2_listBoolean; + private List a2_listChar; + private List a2_listByte; + private List a2_listShort; + private List a2_listInteger; + private List a2_listLong; + private List a2_listFloat; + private List a2_listDouble; + private List a2_listString; + private List a2_listBigInteger; + private List a2_listBigDecimal; + private List a2_listLocalDate; + private List a2_listLocalTime; + private List a2_listLocalDateTime; + private List a2_listEnm; + private List a2_listB1; + private List a2_listB2; + + /* COLLECTIONS: Arrays */ + private boolean[] a2_arrayPrimBoolean; + private char[] a2_arrayPrimChar; + private byte[] a2_arrayPrimByte; + private short[] a2_arrayPrimShort; + private int[] a2_arrayPrimInteger; + private long[] a2_arrayPrimLong; + private float[] a2_arrayPrimFloat; + private double[] a2_arrayPrimDouble; + private Boolean[] a2_arrayBoolean; + private Character[] a2_arrayChar; + private Byte[] a2_arrayByte; + private Short[] a2_arrayShort; + private Integer[] a2_arrayInteger; + private Long[] a2_arrayLong; + private Float[] a2_arrayFloat; + private Double[] a2_arrayDouble; + private String[] a2_arrayString; + private BigInteger[] a2_arrayBigInteger; + private BigDecimal[] a2_arrayBigDecimal; + private LocalDate[] a2_arrayLocalDate; + private LocalTime[] a2_arrayLocalTime; + private LocalDateTime[] a2_arrayLocalDateTime; + private ExampleEnum[] a2_arrayEnm; + private ExampleConfigurationB1[] a2_arrayB1; + private ExampleConfigurationB2[] a2_arrayB2; + + /* COLLECTIONS: Sets */ + private Set a2_setBoolean; + private Set a2_setChar; + private Set a2_setByte; + private Set a2_setShort; + private Set a2_setInteger; + private Set a2_setLong; + private Set a2_setFloat; + private Set a2_setDouble; + private Set a2_setString; + private Set a2_setBigInteger; + private Set a2_setBigDecimal; + private Set a2_setLocalDate; + private Set a2_setLocalTime; + private Set a2_setLocalDateTime; + private Set a2_setEnm; + private Set a2_setB1; + private Set a2_setB2; + + /* COLLECTIONS: Maps */ + private Map a2_mapBooleanBoolean; + private Map a2_mapCharChar; + private Map a2_mapByteByte; + private Map a2_mapShortShort; + private Map a2_mapIntegerInteger; + private Map a2_mapLongLong; + private Map a2_mapFloatFloat; + private Map a2_mapDoubleDouble; + private Map a2_mapStringString; + private Map a2_mapBigIntegerBigInteger; + private Map a2_mapBigDecimalBigDecimal; + private Map a2_mapLocalDateLocalDate; + private Map a2_mapLocalTimeLocalTime; + private Map a2_mapLocalDateTimeLocalDateTime; + private Map a2_mapEnmEnm; + + private Map a2_mapIntegerB1; + private Map a2_mapEnmB2; + + /* COLLECTIONS: Empty */ + private List a2_listEmpty; + private Integer[] a2_arrayEmpty; + private Set a2_setEmpty; + private Map a2_mapEmpty; + + /* COLLECTIONS: Nested */ + private List> a2_listListByte; + private List a2_listArrayFloat; + private List> a2_listSetString; + private List> a2_listMapEnmLocalDate; + + private Set> a2_setSetShort; + private Set a2_setArrayDouble; + private Set> a2_setListString; + private Set> a2_setMapEnmLocalTime; + + private Map> a2_mapIntegerMapLongBoolean; + private Map> a2_mapStringListB1; + private Map a2_mapBigIntegerArrayBigDecimal; + private Map> a2_mapEnmSetB2; + + private Map>>> + a2_mapIntegerListMapShortSetB2; + + private boolean[][] a2_arrayArrayPrimBoolean; + private char[][] a2_arrayArrayPrimChar; + private byte[][] a2_arrayArrayPrimByte; + private short[][] a2_arrayArrayPrimShort; + private int[][] a2_arrayArrayPrimInteger; + private long[][] a2_arrayArrayPrimLong; + private float[][] a2_arrayArrayPrimFloat; + private double[][] a2_arrayArrayPrimDouble; + private Boolean[][] a2_arrayArrayBoolean; + private Character[][] a2_arrayArrayChar; + private Byte[][] a2_arrayArrayByte; + private Short[][] a2_arrayArrayShort; + private Integer[][] a2_arrayArrayInteger; + private Long[][] a2_arrayArrayLong; + private Float[][] a2_arrayArrayFloat; + private Double[][] a2_arrayArrayDouble; + private String[][] a2_arrayArrayString; + private BigInteger[][] a2_arrayArrayBigInteger; + private BigDecimal[][] a2_arrayArrayBigDecimal; + private LocalDate[][] a2_arrayArrayLocalDate; + private LocalTime[][] a2_arrayArrayLocalTime; + private LocalDateTime[][] a2_arrayArrayLocalDateTime; + private ExampleEnum[][] a2_arrayArrayEnm; + private ExampleConfigurationB1[][] a2_arrayArrayB1; + private ExampleConfigurationB2[][] a2_arrayArrayB2; + + /* CUSTOM CONVERTERS */ + private Point a2_point; + private List a2_listPoint; + private Point[] a2_arrayPoint; + private Set a2_setPoint; + private Map> a2_mapEnmListPoint; + + /* UNSUPPORTED TYPES */ +// private Map unsupported; // invalid map key +// private Map, String> unsupported; // invalid map key +// private Box unsupported; // custom parameterized type +// private List unsupported; // wildcard type +// private List unsupported; // wildcard type +// private List[] unsupported; // wildcard type +// private T unsupported; // type variable +// private List unsupported; // raw type +// private List[] unsupported; // raw type +// private List[] unsupported; // generic array type +// private Set[] unsupported; // generic array type +// private Map[] unsupported; // generic array type + + public static int getA2_staticFinalInt() { + return a2_staticFinalInt; + } + + public static int getA2_staticInt() { + return a2_staticInt; + } + + public static void setA2_staticInt(int a2_staticInt) { + ExampleConfigurationA2.a2_staticInt = a2_staticInt; + } + + public int getA2_finalInt() { + return a2_finalInt; + } + + public int getA2_transientInt() { + return a2_transientInt; + } + + public void setA2_transientInt(int a2_transientInt) { + this.a2_transientInt = a2_transientInt; + } + + public int getA2_ignoredInt() { + return a2_ignoredInt; + } + + public void setA2_ignoredInt(int a2_ignoredInt) { + this.a2_ignoredInt = a2_ignoredInt; + } + + public String getA2_ignoredString() { + return a2_ignoredString; + } + + public void setA2_ignoredString(String a2_ignoredString) { + this.a2_ignoredString = a2_ignoredString; + } + + public List getA2_ignoredListString() { + return a2_ignoredListString; + } + + public void setA2_ignoredListString(List a2_ignoredListString) { + this.a2_ignoredListString = a2_ignoredListString; + } + + public boolean isA2_primBool() { + return a2_primBool; + } + + public void setA2_primBool(boolean a2_primBool) { + this.a2_primBool = a2_primBool; + } + + public char getA2_primChar() { + return a2_primChar; + } + + public void setA2_primChar(char a2_primChar) { + this.a2_primChar = a2_primChar; + } + + public byte getA2_primByte() { + return a2_primByte; + } + + public void setA2_primByte(byte a2_primByte) { + this.a2_primByte = a2_primByte; + } + + public short getA2_primShort() { + return a2_primShort; + } + + public void setA2_primShort(short a2_primShort) { + this.a2_primShort = a2_primShort; + } + + public int getA2_primInt() { + return a2_primInt; + } + + public void setA2_primInt(int a2_primInt) { + this.a2_primInt = a2_primInt; + } + + public long getA2_primLong() { + return a2_primLong; + } + + public void setA2_primLong(long a2_primLong) { + this.a2_primLong = a2_primLong; + } + + public float getA2_primFloat() { + return a2_primFloat; + } + + public void setA2_primFloat(float a2_primFloat) { + this.a2_primFloat = a2_primFloat; + } + + public double getA2_primDouble() { + return a2_primDouble; + } + + public void setA2_primDouble(double a2_primDouble) { + this.a2_primDouble = a2_primDouble; + } + + public Boolean getA2_refBool() { + return a2_refBool; + } + + public void setA2_refBool(Boolean a2_refBool) { + this.a2_refBool = a2_refBool; + } + + public Character getA2_refChar() { + return a2_refChar; + } + + public void setA2_refChar(Character a2_refChar) { + this.a2_refChar = a2_refChar; + } + + public Byte getA2_refByte() { + return a2_refByte; + } + + public void setA2_refByte(Byte a2_refByte) { + this.a2_refByte = a2_refByte; + } + + public Short getA2_refShort() { + return a2_refShort; + } + + public void setA2_refShort(Short a2_refShort) { + this.a2_refShort = a2_refShort; + } + + public Integer getA2_refInt() { + return a2_refInt; + } + + public void setA2_refInt(Integer a2_refInt) { + this.a2_refInt = a2_refInt; + } + + public Long getA2_refLong() { + return a2_refLong; + } + + public void setA2_refLong(Long a2_refLong) { + this.a2_refLong = a2_refLong; + } + + public Float getA2_refFloat() { + return a2_refFloat; + } + + public void setA2_refFloat(Float a2_refFloat) { + this.a2_refFloat = a2_refFloat; + } + + public Double getA2_refDouble() { + return a2_refDouble; + } + + public void setA2_refDouble(Double a2_refDouble) { + this.a2_refDouble = a2_refDouble; + } + + public String getA2_string() { + return a2_string; + } + + public void setA2_string(String a2_string) { + this.a2_string = a2_string; + } + + public BigInteger getA2_bigInteger() { + return a2_bigInteger; + } + + public void setA2_bigInteger(BigInteger a2_bigInteger) { + this.a2_bigInteger = a2_bigInteger; + } + + public BigDecimal getA2_bigDecimal() { + return a2_bigDecimal; + } + + public void setA2_bigDecimal(BigDecimal a2_bigDecimal) { + this.a2_bigDecimal = a2_bigDecimal; + } + + public LocalDate getA2_localDate() { + return a2_localDate; + } + + public void setA2_localDate(LocalDate a2_localDate) { + this.a2_localDate = a2_localDate; + } + + public LocalTime getA2_localTime() { + return a2_localTime; + } + + public void setA2_localTime(LocalTime a2_localTime) { + this.a2_localTime = a2_localTime; + } + + public LocalDateTime getA2_localDateTime() { + return a2_localDateTime; + } + + public void setA2_localDateTime(LocalDateTime a2_localDateTime) { + this.a2_localDateTime = a2_localDateTime; + } + + public ExampleEnum getA2_Enm() { + return a2_Enm; + } + + public void setA2_Enm(ExampleEnum a2_Enm) { + this.a2_Enm = a2_Enm; + } + + public ExampleConfigurationB1 getA2_b1() { + return a2_b1; + } + + public void setA2_b1(ExampleConfigurationB1 a2_b1) { + this.a2_b1 = a2_b1; + } + + public ExampleConfigurationB2 getA2_b2() { + return a2_b2; + } + + public void setA2_b2(ExampleConfigurationB2 a2_b2) { + this.a2_b2 = a2_b2; + } + + public List getA2_listBoolean() { + return a2_listBoolean; + } + + public void setA2_listBoolean(List a2_listBoolean) { + this.a2_listBoolean = a2_listBoolean; + } + + public List getA2_listChar() { + return a2_listChar; + } + + public void setA2_listChar(List a2_listChar) { + this.a2_listChar = a2_listChar; + } + + public List getA2_listByte() { + return a2_listByte; + } + + public void setA2_listByte(List a2_listByte) { + this.a2_listByte = a2_listByte; + } + + public List getA2_listShort() { + return a2_listShort; + } + + public void setA2_listShort(List a2_listShort) { + this.a2_listShort = a2_listShort; + } + + public List getA2_listInteger() { + return a2_listInteger; + } + + public void setA2_listInteger(List a2_listInteger) { + this.a2_listInteger = a2_listInteger; + } + + public List getA2_listLong() { + return a2_listLong; + } + + public void setA2_listLong(List a2_listLong) { + this.a2_listLong = a2_listLong; + } + + public List getA2_listFloat() { + return a2_listFloat; + } + + public void setA2_listFloat(List a2_listFloat) { + this.a2_listFloat = a2_listFloat; + } + + public List getA2_listDouble() { + return a2_listDouble; + } + + public void setA2_listDouble(List a2_listDouble) { + this.a2_listDouble = a2_listDouble; + } + + public List getA2_listString() { + return a2_listString; + } + + public void setA2_listString(List a2_listString) { + this.a2_listString = a2_listString; + } + + public List getA2_listBigInteger() { + return a2_listBigInteger; + } + + public void setA2_listBigInteger(List a2_listBigInteger) { + this.a2_listBigInteger = a2_listBigInteger; + } + + public List getA2_listBigDecimal() { + return a2_listBigDecimal; + } + + public void setA2_listBigDecimal(List a2_listBigDecimal) { + this.a2_listBigDecimal = a2_listBigDecimal; + } + + public List getA2_listLocalDate() { + return a2_listLocalDate; + } + + public void setA2_listLocalDate(List a2_listLocalDate) { + this.a2_listLocalDate = a2_listLocalDate; + } + + public List getA2_listLocalTime() { + return a2_listLocalTime; + } + + public void setA2_listLocalTime(List a2_listLocalTime) { + this.a2_listLocalTime = a2_listLocalTime; + } + + public List getA2_listLocalDateTime() { + return a2_listLocalDateTime; + } + + public void setA2_listLocalDateTime(List a2_listLocalDateTime) { + this.a2_listLocalDateTime = a2_listLocalDateTime; + } + + public List getA2_listEnm() { + return a2_listEnm; + } + + public void setA2_listEnm(List a2_listEnm) { + this.a2_listEnm = a2_listEnm; + } + + public List getA2_listB1() { + return a2_listB1; + } + + public void setA2_listB1(List a2_listB1) { + this.a2_listB1 = a2_listB1; + } + + public List getA2_listB2() { + return a2_listB2; + } + + public void setA2_listB2(List a2_listB2) { + this.a2_listB2 = a2_listB2; + } + + public boolean[] getA2_arrayPrimBoolean() { + return a2_arrayPrimBoolean; + } + + public void setA2_arrayPrimBoolean(boolean[] a2_arrayPrimBoolean) { + this.a2_arrayPrimBoolean = a2_arrayPrimBoolean; + } + + public char[] getA2_arrayPrimChar() { + return a2_arrayPrimChar; + } + + public void setA2_arrayPrimChar(char[] a2_arrayPrimChar) { + this.a2_arrayPrimChar = a2_arrayPrimChar; + } + + public byte[] getA2_arrayPrimByte() { + return a2_arrayPrimByte; + } + + public void setA2_arrayPrimByte(byte[] a2_arrayPrimByte) { + this.a2_arrayPrimByte = a2_arrayPrimByte; + } + + public short[] getA2_arrayPrimShort() { + return a2_arrayPrimShort; + } + + public void setA2_arrayPrimShort(short[] a2_arrayPrimShort) { + this.a2_arrayPrimShort = a2_arrayPrimShort; + } + + public int[] getA2_arrayPrimInteger() { + return a2_arrayPrimInteger; + } + + public void setA2_arrayPrimInteger(int[] a2_arrayPrimInteger) { + this.a2_arrayPrimInteger = a2_arrayPrimInteger; + } + + public long[] getA2_arrayPrimLong() { + return a2_arrayPrimLong; + } + + public void setA2_arrayPrimLong(long[] a2_arrayPrimLong) { + this.a2_arrayPrimLong = a2_arrayPrimLong; + } + + public float[] getA2_arrayPrimFloat() { + return a2_arrayPrimFloat; + } + + public void setA2_arrayPrimFloat(float[] a2_arrayPrimFloat) { + this.a2_arrayPrimFloat = a2_arrayPrimFloat; + } + + public double[] getA2_arrayPrimDouble() { + return a2_arrayPrimDouble; + } + + public void setA2_arrayPrimDouble(double[] a2_arrayPrimDouble) { + this.a2_arrayPrimDouble = a2_arrayPrimDouble; + } + + public Boolean[] getA2_arrayBoolean() { + return a2_arrayBoolean; + } + + public void setA2_arrayBoolean(Boolean[] a2_arrayBoolean) { + this.a2_arrayBoolean = a2_arrayBoolean; + } + + public Character[] getA2_arrayChar() { + return a2_arrayChar; + } + + public void setA2_arrayChar(Character[] a2_arrayChar) { + this.a2_arrayChar = a2_arrayChar; + } + + public Byte[] getA2_arrayByte() { + return a2_arrayByte; + } + + public void setA2_arrayByte(Byte[] a2_arrayByte) { + this.a2_arrayByte = a2_arrayByte; + } + + public Short[] getA2_arrayShort() { + return a2_arrayShort; + } + + public void setA2_arrayShort(Short[] a2_arrayShort) { + this.a2_arrayShort = a2_arrayShort; + } + + public Integer[] getA2_arrayInteger() { + return a2_arrayInteger; + } + + public void setA2_arrayInteger(Integer[] a2_arrayInteger) { + this.a2_arrayInteger = a2_arrayInteger; + } + + public Long[] getA2_arrayLong() { + return a2_arrayLong; + } + + public void setA2_arrayLong(Long[] a2_arrayLong) { + this.a2_arrayLong = a2_arrayLong; + } + + public Float[] getA2_arrayFloat() { + return a2_arrayFloat; + } + + public void setA2_arrayFloat(Float[] a2_arrayFloat) { + this.a2_arrayFloat = a2_arrayFloat; + } + + public Double[] getA2_arrayDouble() { + return a2_arrayDouble; + } + + public void setA2_arrayDouble(Double[] a2_arrayDouble) { + this.a2_arrayDouble = a2_arrayDouble; + } + + public String[] getA2_arrayString() { + return a2_arrayString; + } + + public void setA2_arrayString(String[] a2_arrayString) { + this.a2_arrayString = a2_arrayString; + } + + public BigInteger[] getA2_arrayBigInteger() { + return a2_arrayBigInteger; + } + + public void setA2_arrayBigInteger(BigInteger[] a2_arrayBigInteger) { + this.a2_arrayBigInteger = a2_arrayBigInteger; + } + + public BigDecimal[] getA2_arrayBigDecimal() { + return a2_arrayBigDecimal; + } + + public void setA2_arrayBigDecimal(BigDecimal[] a2_arrayBigDecimal) { + this.a2_arrayBigDecimal = a2_arrayBigDecimal; + } + + public LocalDate[] getA2_arrayLocalDate() { + return a2_arrayLocalDate; + } + + public void setA2_arrayLocalDate(LocalDate[] a2_arrayLocalDate) { + this.a2_arrayLocalDate = a2_arrayLocalDate; + } + + public LocalTime[] getA2_arrayLocalTime() { + return a2_arrayLocalTime; + } + + public void setA2_arrayLocalTime(LocalTime[] a2_arrayLocalTime) { + this.a2_arrayLocalTime = a2_arrayLocalTime; + } + + public LocalDateTime[] getA2_arrayLocalDateTime() { + return a2_arrayLocalDateTime; + } + + public void setA2_arrayLocalDateTime(LocalDateTime[] a2_arrayLocalDateTime) { + this.a2_arrayLocalDateTime = a2_arrayLocalDateTime; + } + + public ExampleEnum[] getA2_arrayEnm() { + return a2_arrayEnm; + } + + public void setA2_arrayEnm(ExampleEnum[] a2_arrayEnm) { + this.a2_arrayEnm = a2_arrayEnm; + } + + public ExampleConfigurationB1[] getA2_arrayB1() { + return a2_arrayB1; + } + + public void setA2_arrayB1(ExampleConfigurationB1[] a2_arrayB1) { + this.a2_arrayB1 = a2_arrayB1; + } + + public ExampleConfigurationB2[] getA2_arrayB2() { + return a2_arrayB2; + } + + public void setA2_arrayB2(ExampleConfigurationB2[] a2_arrayB2) { + this.a2_arrayB2 = a2_arrayB2; + } + + public Set getA2_setBoolean() { + return a2_setBoolean; + } + + public void setA2_setBoolean(Set a2_setBoolean) { + this.a2_setBoolean = a2_setBoolean; + } + + public Set getA2_setChar() { + return a2_setChar; + } + + public void setA2_setChar(Set a2_setChar) { + this.a2_setChar = a2_setChar; + } + + public Set getA2_setByte() { + return a2_setByte; + } + + public void setA2_setByte(Set a2_setByte) { + this.a2_setByte = a2_setByte; + } + + public Set getA2_setShort() { + return a2_setShort; + } + + public void setA2_setShort(Set a2_setShort) { + this.a2_setShort = a2_setShort; + } + + public Set getA2_setInteger() { + return a2_setInteger; + } + + public void setA2_setInteger(Set a2_setInteger) { + this.a2_setInteger = a2_setInteger; + } + + public Set getA2_setLong() { + return a2_setLong; + } + + public void setA2_setLong(Set a2_setLong) { + this.a2_setLong = a2_setLong; + } + + public Set getA2_setFloat() { + return a2_setFloat; + } + + public void setA2_setFloat(Set a2_setFloat) { + this.a2_setFloat = a2_setFloat; + } + + public Set getA2_setDouble() { + return a2_setDouble; + } + + public void setA2_setDouble(Set a2_setDouble) { + this.a2_setDouble = a2_setDouble; + } + + public Set getA2_setString() { + return a2_setString; + } + + public void setA2_setString(Set a2_setString) { + this.a2_setString = a2_setString; + } + + public Set getA2_setBigInteger() { + return a2_setBigInteger; + } + + public void setA2_setBigInteger(Set a2_setBigInteger) { + this.a2_setBigInteger = a2_setBigInteger; + } + + public Set getA2_setBigDecimal() { + return a2_setBigDecimal; + } + + public void setA2_setBigDecimal(Set a2_setBigDecimal) { + this.a2_setBigDecimal = a2_setBigDecimal; + } + + public Set getA2_setLocalDate() { + return a2_setLocalDate; + } + + public void setA2_setLocalDate(Set a2_setLocalDate) { + this.a2_setLocalDate = a2_setLocalDate; + } + + public Set getA2_setLocalTime() { + return a2_setLocalTime; + } + + public void setA2_setLocalTime(Set a2_setLocalTime) { + this.a2_setLocalTime = a2_setLocalTime; + } + + public Set getA2_setLocalDateTime() { + return a2_setLocalDateTime; + } + + public void setA2_setLocalDateTime(Set a2_setLocalDateTime) { + this.a2_setLocalDateTime = a2_setLocalDateTime; + } + + public Set getA2_setEnm() { + return a2_setEnm; + } + + public void setA2_setEnm(Set a2_setEnm) { + this.a2_setEnm = a2_setEnm; + } + + public Set getA2_setB1() { + return a2_setB1; + } + + public void setA2_setB1(Set a2_setB1) { + this.a2_setB1 = a2_setB1; + } + + public Set getA2_setB2() { + return a2_setB2; + } + + public void setA2_setB2(Set a2_setB2) { + this.a2_setB2 = a2_setB2; + } + + public Map getA2_mapBooleanBoolean() { + return a2_mapBooleanBoolean; + } + + public void setA2_mapBooleanBoolean(Map a2_mapBooleanBoolean) { + this.a2_mapBooleanBoolean = a2_mapBooleanBoolean; + } + + public Map getA2_mapCharChar() { + return a2_mapCharChar; + } + + public void setA2_mapCharChar(Map a2_mapCharChar) { + this.a2_mapCharChar = a2_mapCharChar; + } + + public Map getA2_mapByteByte() { + return a2_mapByteByte; + } + + public void setA2_mapByteByte(Map a2_mapByteByte) { + this.a2_mapByteByte = a2_mapByteByte; + } + + public Map getA2_mapShortShort() { + return a2_mapShortShort; + } + + public void setA2_mapShortShort(Map a2_mapShortShort) { + this.a2_mapShortShort = a2_mapShortShort; + } + + public Map getA2_mapIntegerInteger() { + return a2_mapIntegerInteger; + } + + public void setA2_mapIntegerInteger(Map a2_mapIntegerInteger) { + this.a2_mapIntegerInteger = a2_mapIntegerInteger; + } + + public Map getA2_mapLongLong() { + return a2_mapLongLong; + } + + public void setA2_mapLongLong(Map a2_mapLongLong) { + this.a2_mapLongLong = a2_mapLongLong; + } + + public Map getA2_mapFloatFloat() { + return a2_mapFloatFloat; + } + + public void setA2_mapFloatFloat(Map a2_mapFloatFloat) { + this.a2_mapFloatFloat = a2_mapFloatFloat; + } + + public Map getA2_mapDoubleDouble() { + return a2_mapDoubleDouble; + } + + public void setA2_mapDoubleDouble(Map a2_mapDoubleDouble) { + this.a2_mapDoubleDouble = a2_mapDoubleDouble; + } + + public Map getA2_mapStringString() { + return a2_mapStringString; + } + + public void setA2_mapStringString(Map a2_mapStringString) { + this.a2_mapStringString = a2_mapStringString; + } + + public Map getA2_mapBigIntegerBigInteger() { + return a2_mapBigIntegerBigInteger; + } + + public void setA2_mapBigIntegerBigInteger(Map a2_mapBigIntegerBigInteger) { + this.a2_mapBigIntegerBigInteger = a2_mapBigIntegerBigInteger; + } + + public Map getA2_mapBigDecimalBigDecimal() { + return a2_mapBigDecimalBigDecimal; + } + + public void setA2_mapBigDecimalBigDecimal(Map a2_mapBigDecimalBigDecimal) { + this.a2_mapBigDecimalBigDecimal = a2_mapBigDecimalBigDecimal; + } + + public Map getA2_mapLocalDateLocalDate() { + return a2_mapLocalDateLocalDate; + } + + public void setA2_mapLocalDateLocalDate(Map a2_mapLocalDateLocalDate) { + this.a2_mapLocalDateLocalDate = a2_mapLocalDateLocalDate; + } + + public Map getA2_mapLocalTimeLocalTime() { + return a2_mapLocalTimeLocalTime; + } + + public void setA2_mapLocalTimeLocalTime(Map a2_mapLocalTimeLocalTime) { + this.a2_mapLocalTimeLocalTime = a2_mapLocalTimeLocalTime; + } + + public Map getA2_mapLocalDateTimeLocalDateTime() { + return a2_mapLocalDateTimeLocalDateTime; + } + + public void setA2_mapLocalDateTimeLocalDateTime(Map a2_mapLocalDateTimeLocalDateTime) { + this.a2_mapLocalDateTimeLocalDateTime = a2_mapLocalDateTimeLocalDateTime; + } + + public Map getA2_mapEnmEnm() { + return a2_mapEnmEnm; + } + + public void setA2_mapEnmEnm(Map a2_mapEnmEnm) { + this.a2_mapEnmEnm = a2_mapEnmEnm; + } + + public Map getA2_mapIntegerB1() { + return a2_mapIntegerB1; + } + + public void setA2_mapIntegerB1(Map a2_mapIntegerB1) { + this.a2_mapIntegerB1 = a2_mapIntegerB1; + } + + public Map getA2_mapEnmB2() { + return a2_mapEnmB2; + } + + public void setA2_mapEnmB2(Map a2_mapEnmB2) { + this.a2_mapEnmB2 = a2_mapEnmB2; + } + + public List getA2_listEmpty() { + return a2_listEmpty; + } + + public void setA2_listEmpty(List a2_listEmpty) { + this.a2_listEmpty = a2_listEmpty; + } + + public Integer[] getA2_arrayEmpty() { + return a2_arrayEmpty; + } + + public void setA2_arrayEmpty(Integer[] a2_arrayEmpty) { + this.a2_arrayEmpty = a2_arrayEmpty; + } + + public Set getA2_setEmpty() { + return a2_setEmpty; + } + + public void setA2_setEmpty(Set a2_setEmpty) { + this.a2_setEmpty = a2_setEmpty; + } + + public Map getA2_mapEmpty() { + return a2_mapEmpty; + } + + public void setA2_mapEmpty(Map a2_mapEmpty) { + this.a2_mapEmpty = a2_mapEmpty; + } + + public List> getA2_listListByte() { + return a2_listListByte; + } + + public void setA2_listListByte(List> a2_listListByte) { + this.a2_listListByte = a2_listListByte; + } + + public List getA2_listArrayFloat() { + return a2_listArrayFloat; + } + + public void setA2_listArrayFloat(List a2_listArrayFloat) { + this.a2_listArrayFloat = a2_listArrayFloat; + } + + public List> getA2_listSetString() { + return a2_listSetString; + } + + public void setA2_listSetString(List> a2_listSetString) { + this.a2_listSetString = a2_listSetString; + } + + public List> getA2_listMapEnmLocalDate() { + return a2_listMapEnmLocalDate; + } + + public void setA2_listMapEnmLocalDate(List> a2_listMapEnmLocalDate) { + this.a2_listMapEnmLocalDate = a2_listMapEnmLocalDate; + } + + public Set> getA2_setSetShort() { + return a2_setSetShort; + } + + public void setA2_setSetShort(Set> a2_setSetShort) { + this.a2_setSetShort = a2_setSetShort; + } + + public Set getA2_setArrayDouble() { + return a2_setArrayDouble; + } + + public void setA2_setArrayDouble(Set a2_setArrayDouble) { + this.a2_setArrayDouble = a2_setArrayDouble; + } + + public Set> getA2_setListString() { + return a2_setListString; + } + + public void setA2_setListString(Set> a2_setListString) { + this.a2_setListString = a2_setListString; + } + + public Set> getA2_setMapEnmLocalTime() { + return a2_setMapEnmLocalTime; + } + + public void setA2_setMapEnmLocalTime(Set> a2_setMapEnmLocalTime) { + this.a2_setMapEnmLocalTime = a2_setMapEnmLocalTime; + } + + public Map> getA2_mapIntegerMapLongBoolean() { + return a2_mapIntegerMapLongBoolean; + } + + public void setA2_mapIntegerMapLongBoolean(Map> a2_mapIntegerMapLongBoolean) { + this.a2_mapIntegerMapLongBoolean = a2_mapIntegerMapLongBoolean; + } + + public Map> getA2_mapStringListB1() { + return a2_mapStringListB1; + } + + public void setA2_mapStringListB1(Map> a2_mapStringListB1) { + this.a2_mapStringListB1 = a2_mapStringListB1; + } + + public Map getA2_mapBigIntegerArrayBigDecimal() { + return a2_mapBigIntegerArrayBigDecimal; + } + + public void setA2_mapBigIntegerArrayBigDecimal(Map a2_mapBigIntegerArrayBigDecimal) { + this.a2_mapBigIntegerArrayBigDecimal = a2_mapBigIntegerArrayBigDecimal; + } + + public Map> getA2_mapEnmSetB2() { + return a2_mapEnmSetB2; + } + + public void setA2_mapEnmSetB2(Map> a2_mapEnmSetB2) { + this.a2_mapEnmSetB2 = a2_mapEnmSetB2; + } + + public Map>>> getA2_mapIntegerListMapShortSetB2() { + return a2_mapIntegerListMapShortSetB2; + } + + public void setA2_mapIntegerListMapShortSetB2(Map>>> a2_mapIntegerListMapShortSetB2) { + this.a2_mapIntegerListMapShortSetB2 = a2_mapIntegerListMapShortSetB2; + } + + public boolean[][] getA2_arrayArrayPrimBoolean() { + return a2_arrayArrayPrimBoolean; + } + + public void setA2_arrayArrayPrimBoolean(boolean[][] a2_arrayArrayPrimBoolean) { + this.a2_arrayArrayPrimBoolean = a2_arrayArrayPrimBoolean; + } + + public char[][] getA2_arrayArrayPrimChar() { + return a2_arrayArrayPrimChar; + } + + public void setA2_arrayArrayPrimChar(char[][] a2_arrayArrayPrimChar) { + this.a2_arrayArrayPrimChar = a2_arrayArrayPrimChar; + } + + public byte[][] getA2_arrayArrayPrimByte() { + return a2_arrayArrayPrimByte; + } + + public void setA2_arrayArrayPrimByte(byte[][] a2_arrayArrayPrimByte) { + this.a2_arrayArrayPrimByte = a2_arrayArrayPrimByte; + } + + public short[][] getA2_arrayArrayPrimShort() { + return a2_arrayArrayPrimShort; + } + + public void setA2_arrayArrayPrimShort(short[][] a2_arrayArrayPrimShort) { + this.a2_arrayArrayPrimShort = a2_arrayArrayPrimShort; + } + + public int[][] getA2_arrayArrayPrimInteger() { + return a2_arrayArrayPrimInteger; + } + + public void setA2_arrayArrayPrimInteger(int[][] a2_arrayArrayPrimInteger) { + this.a2_arrayArrayPrimInteger = a2_arrayArrayPrimInteger; + } + + public long[][] getA2_arrayArrayPrimLong() { + return a2_arrayArrayPrimLong; + } + + public void setA2_arrayArrayPrimLong(long[][] a2_arrayArrayPrimLong) { + this.a2_arrayArrayPrimLong = a2_arrayArrayPrimLong; + } + + public float[][] getA2_arrayArrayPrimFloat() { + return a2_arrayArrayPrimFloat; + } + + public void setA2_arrayArrayPrimFloat(float[][] a2_arrayArrayPrimFloat) { + this.a2_arrayArrayPrimFloat = a2_arrayArrayPrimFloat; + } + + public double[][] getA2_arrayArrayPrimDouble() { + return a2_arrayArrayPrimDouble; + } + + public void setA2_arrayArrayPrimDouble(double[][] a2_arrayArrayPrimDouble) { + this.a2_arrayArrayPrimDouble = a2_arrayArrayPrimDouble; + } + + public Boolean[][] getA2_arrayArrayBoolean() { + return a2_arrayArrayBoolean; + } + + public void setA2_arrayArrayBoolean(Boolean[][] a2_arrayArrayBoolean) { + this.a2_arrayArrayBoolean = a2_arrayArrayBoolean; + } + + public Character[][] getA2_arrayArrayChar() { + return a2_arrayArrayChar; + } + + public void setA2_arrayArrayChar(Character[][] a2_arrayArrayChar) { + this.a2_arrayArrayChar = a2_arrayArrayChar; + } + + public Byte[][] getA2_arrayArrayByte() { + return a2_arrayArrayByte; + } + + public void setA2_arrayArrayByte(Byte[][] a2_arrayArrayByte) { + this.a2_arrayArrayByte = a2_arrayArrayByte; + } + + public Short[][] getA2_arrayArrayShort() { + return a2_arrayArrayShort; + } + + public void setA2_arrayArrayShort(Short[][] a2_arrayArrayShort) { + this.a2_arrayArrayShort = a2_arrayArrayShort; + } + + public Integer[][] getA2_arrayArrayInteger() { + return a2_arrayArrayInteger; + } + + public void setA2_arrayArrayInteger(Integer[][] a2_arrayArrayInteger) { + this.a2_arrayArrayInteger = a2_arrayArrayInteger; + } + + public Long[][] getA2_arrayArrayLong() { + return a2_arrayArrayLong; + } + + public void setA2_arrayArrayLong(Long[][] a2_arrayArrayLong) { + this.a2_arrayArrayLong = a2_arrayArrayLong; + } + + public Float[][] getA2_arrayArrayFloat() { + return a2_arrayArrayFloat; + } + + public void setA2_arrayArrayFloat(Float[][] a2_arrayArrayFloat) { + this.a2_arrayArrayFloat = a2_arrayArrayFloat; + } + + public Double[][] getA2_arrayArrayDouble() { + return a2_arrayArrayDouble; + } + + public void setA2_arrayArrayDouble(Double[][] a2_arrayArrayDouble) { + this.a2_arrayArrayDouble = a2_arrayArrayDouble; + } + + public String[][] getA2_arrayArrayString() { + return a2_arrayArrayString; + } + + public void setA2_arrayArrayString(String[][] a2_arrayArrayString) { + this.a2_arrayArrayString = a2_arrayArrayString; + } + + public BigInteger[][] getA2_arrayArrayBigInteger() { + return a2_arrayArrayBigInteger; + } + + public void setA2_arrayArrayBigInteger(BigInteger[][] a2_arrayArrayBigInteger) { + this.a2_arrayArrayBigInteger = a2_arrayArrayBigInteger; + } + + public BigDecimal[][] getA2_arrayArrayBigDecimal() { + return a2_arrayArrayBigDecimal; + } + + public void setA2_arrayArrayBigDecimal(BigDecimal[][] a2_arrayArrayBigDecimal) { + this.a2_arrayArrayBigDecimal = a2_arrayArrayBigDecimal; + } + + public LocalDate[][] getA2_arrayArrayLocalDate() { + return a2_arrayArrayLocalDate; + } + + public void setA2_arrayArrayLocalDate(LocalDate[][] a2_arrayArrayLocalDate) { + this.a2_arrayArrayLocalDate = a2_arrayArrayLocalDate; + } + + public LocalTime[][] getA2_arrayArrayLocalTime() { + return a2_arrayArrayLocalTime; + } + + public void setA2_arrayArrayLocalTime(LocalTime[][] a2_arrayArrayLocalTime) { + this.a2_arrayArrayLocalTime = a2_arrayArrayLocalTime; + } + + public LocalDateTime[][] getA2_arrayArrayLocalDateTime() { + return a2_arrayArrayLocalDateTime; + } + + public void setA2_arrayArrayLocalDateTime(LocalDateTime[][] a2_arrayArrayLocalDateTime) { + this.a2_arrayArrayLocalDateTime = a2_arrayArrayLocalDateTime; + } + + public ExampleEnum[][] getA2_arrayArrayEnm() { + return a2_arrayArrayEnm; + } + + public void setA2_arrayArrayEnm(ExampleEnum[][] a2_arrayArrayEnm) { + this.a2_arrayArrayEnm = a2_arrayArrayEnm; + } + + public ExampleConfigurationB1[][] getA2_arrayArrayB1() { + return a2_arrayArrayB1; + } + + public void setA2_arrayArrayB1(ExampleConfigurationB1[][] a2_arrayArrayB1) { + this.a2_arrayArrayB1 = a2_arrayArrayB1; + } + + public ExampleConfigurationB2[][] getA2_arrayArrayB2() { + return a2_arrayArrayB2; + } + + public void setA2_arrayArrayB2(ExampleConfigurationB2[][] a2_arrayArrayB2) { + this.a2_arrayArrayB2 = a2_arrayArrayB2; + } + + public Point getA2_point() { + return a2_point; + } + + public void setA2_point(Point a2_point) { + this.a2_point = a2_point; + } + + public List getA2_listPoint() { + return a2_listPoint; + } + + public void setA2_listPoint(List a2_listPoint) { + this.a2_listPoint = a2_listPoint; + } + + public Point[] getA2_arrayPoint() { + return a2_arrayPoint; + } + + public void setA2_arrayPoint(Point[] a2_arrayPoint) { + this.a2_arrayPoint = a2_arrayPoint; + } + + public Set getA2_setPoint() { + return a2_setPoint; + } + + public void setA2_setPoint(Set a2_setPoint) { + this.a2_setPoint = a2_setPoint; + } + + public Map> getA2_mapEnmListPoint() { + return a2_mapEnmListPoint; + } + + public void setA2_mapEnmListPoint(Map> a2_mapEnmListPoint) { + this.a2_mapEnmListPoint = a2_mapEnmListPoint; + } +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationB1.java b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationB1.java new file mode 100644 index 0000000..d5cfd52 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationB1.java @@ -0,0 +1,188 @@ +package de.exlll.configlib.configurations; + +import de.exlll.configlib.Configuration; +import de.exlll.configlib.Ignore; + +import java.awt.Point; +import java.util.*; + +@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal"}) +@Configuration +public class ExampleConfigurationB1 { + /* IGNORED FIELDS */ + private static final int b1_staticFinalInt = 1; + private static int b1_staticInt = 2; + private final int b1_finalInt = 3; + private transient int b1_transientInt = 4; + @Ignore + private int b1_ignoredInt = 5; + @Ignore + private String b1_ignoredString = "ignoredString"; + @Ignore + private List b1_ignoredListString = List.of("ignored", "list", "string"); + + private boolean b1_primBool; + private Character b1_refChar; + private String b1_string; + private List b1_listByte; + private Short[] b1_arrayShort; + private Set b1_setInteger; + private List b1_listEmpty; + private Map b1_mapLongLong; + private List> b1_listListByte; + private Point b1_point; + + public static int getB1_staticFinalInt() { + return b1_staticFinalInt; + } + + public static int getB1_staticInt() { + return b1_staticInt; + } + + public static void setB1_staticInt(int b1_staticInt) { + ExampleConfigurationB1.b1_staticInt = b1_staticInt; + } + + public int getB1_finalInt() { + return b1_finalInt; + } + + public int getB1_transientInt() { + return b1_transientInt; + } + + public void setB1_transientInt(int b1_transientInt) { + this.b1_transientInt = b1_transientInt; + } + + public int getB1_ignoredInt() { + return b1_ignoredInt; + } + + public void setB1_ignoredInt(int b1_ignoredInt) { + this.b1_ignoredInt = b1_ignoredInt; + } + + public String getB1_ignoredString() { + return b1_ignoredString; + } + + public void setB1_ignoredString(String b1_ignoredString) { + this.b1_ignoredString = b1_ignoredString; + } + + public List getB1_ignoredListString() { + return b1_ignoredListString; + } + + public void setB1_ignoredListString(List b1_ignoredListString) { + this.b1_ignoredListString = b1_ignoredListString; + } + + public boolean isB1_primBool() { + return b1_primBool; + } + + public void setB1_primBool(boolean b1_primBool) { + this.b1_primBool = b1_primBool; + } + + public Character getB1_refChar() { + return b1_refChar; + } + + public void setB1_refChar(Character b1_refChar) { + this.b1_refChar = b1_refChar; + } + + public String getB1_string() { + return b1_string; + } + + public void setB1_string(String b1_string) { + this.b1_string = b1_string; + } + + public List getB1_listByte() { + return b1_listByte; + } + + public void setB1_listByte(List b1_listByte) { + this.b1_listByte = b1_listByte; + } + + public Short[] getB1_arrayShort() { + return b1_arrayShort; + } + + public void setB1_arrayShort(Short[] b1_arrayShort) { + this.b1_arrayShort = b1_arrayShort; + } + + public Set getB1_setInteger() { + return b1_setInteger; + } + + public void setB1_setInteger(Set b1_setInteger) { + this.b1_setInteger = b1_setInteger; + } + + public List getB1_listEmpty() { + return b1_listEmpty; + } + + public void setB1_listEmpty(List b1_listEmpty) { + this.b1_listEmpty = b1_listEmpty; + } + + public Map getB1_mapLongLong() { + return b1_mapLongLong; + } + + public void setB1_mapLongLong(Map b1_mapLongLong) { + this.b1_mapLongLong = b1_mapLongLong; + } + + public List> getB1_listListByte() { + return b1_listListByte; + } + + public void setB1_listListByte(List> b1_listListByte) { + this.b1_listListByte = b1_listListByte; + } + + public Point getB1_point() { + return b1_point; + } + + public void setB1_point(Point b1_point) { + this.b1_point = b1_point; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ExampleConfigurationB1 that = (ExampleConfigurationB1) o; + return b1_transientInt == that.b1_transientInt && + b1_ignoredInt == that.b1_ignoredInt && + b1_primBool == that.b1_primBool && + Objects.equals(b1_ignoredString, that.b1_ignoredString) && + Objects.equals(b1_ignoredListString, that.b1_ignoredListString) && + Objects.equals(b1_refChar, that.b1_refChar) && + Objects.equals(b1_string, that.b1_string) && + Objects.equals(b1_listByte, that.b1_listByte) && + Arrays.equals(b1_arrayShort, that.b1_arrayShort) && + Objects.equals(b1_setInteger, that.b1_setInteger) && + Objects.equals(b1_listEmpty, that.b1_listEmpty) && + Objects.equals(b1_mapLongLong, that.b1_mapLongLong) && + Objects.equals(b1_listListByte, that.b1_listListByte) && + Objects.equals(b1_point, that.b1_point); + } + + @Override + public int hashCode() { + return 0; + } +} \ No newline at end of file diff --git a/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationB2.java b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationB2.java new file mode 100644 index 0000000..33bfe83 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationB2.java @@ -0,0 +1,192 @@ +package de.exlll.configlib.configurations; + +import de.exlll.configlib.Ignore; + +import java.awt.Point; +import java.math.BigInteger; +import java.util.*; + +import static de.exlll.configlib.TestUtils.collectionOfArraysDeepEquals; + +@SuppressWarnings({"FieldCanBeLocal", "FieldMayBeFinal"}) +public final class ExampleConfigurationB2 extends ExampleConfigurationB1 { + /* IGNORED FIELDS */ + private static final int b2_staticFinalInt = 1; + private static int b2_staticInt = 2; + private final int b2_finalInt = 3; + private transient int b2_transientInt = 4; + @Ignore + private int b2_ignoredInt = 5; + @Ignore + private String b2_ignoredString = "ignoredString"; + @Ignore + private List b2_ignoredListString = List.of("ignored", "list", "string"); + + /* PRIMITIVE TYPES */ + private char b2_primChar; + private Boolean b2_refBool; + private BigInteger b2_bigInteger; + private List b2_listShort; + private Integer[] b2_arrayInteger; + private Set b2_setLong; + private BigInteger[] b2_arrayEmpty; + private Map b2_mapFloatFloat; + private Set b2_setArrayDouble; + private List b2_listPoint; + + public static int getB2_staticFinalInt() { + return b2_staticFinalInt; + } + + public static int getB2_staticInt() { + return b2_staticInt; + } + + public static void setB2_staticInt(int b2_staticInt) { + ExampleConfigurationB2.b2_staticInt = b2_staticInt; + } + + public int getB2_finalInt() { + return b2_finalInt; + } + + public int getB2_transientInt() { + return b2_transientInt; + } + + public void setB2_transientInt(int b2_transientInt) { + this.b2_transientInt = b2_transientInt; + } + + public int getB2_ignoredInt() { + return b2_ignoredInt; + } + + // TODO: Set to different value to check that it's really ignored + public void setB2_ignoredInt(int b2_ignoredInt) { + this.b2_ignoredInt = b2_ignoredInt; + } + + public String getB2_ignoredString() { + return b2_ignoredString; + } + + public void setB2_ignoredString(String b2_ignoredString) { + this.b2_ignoredString = b2_ignoredString; + } + + public List getB2_ignoredListString() { + return b2_ignoredListString; + } + + public void setB2_ignoredListString(List b2_ignoredListString) { + this.b2_ignoredListString = b2_ignoredListString; + } + + public char getB2_primChar() { + return b2_primChar; + } + + public void setB2_primChar(char b2_primChar) { + this.b2_primChar = b2_primChar; + } + + public Boolean getB2_refBool() { + return b2_refBool; + } + + public void setB2_refBool(Boolean b2_refBool) { + this.b2_refBool = b2_refBool; + } + + public BigInteger getB2_bigInteger() { + return b2_bigInteger; + } + + public void setB2_bigInteger(BigInteger b2_bigInteger) { + this.b2_bigInteger = b2_bigInteger; + } + + public List getB2_listShort() { + return b2_listShort; + } + + public void setB2_listShort(List b2_listShort) { + this.b2_listShort = b2_listShort; + } + + public Integer[] getB2_arrayInteger() { + return b2_arrayInteger; + } + + public void setB2_arrayInteger(Integer[] b2_arrayInteger) { + this.b2_arrayInteger = b2_arrayInteger; + } + + public Set getB2_setLong() { + return b2_setLong; + } + + public void setB2_setLong(Set b2_setLong) { + this.b2_setLong = b2_setLong; + } + + public BigInteger[] getB2_arrayEmpty() { + return b2_arrayEmpty; + } + + public void setB2_arrayEmpty(BigInteger[] b2_arrayEmpty) { + this.b2_arrayEmpty = b2_arrayEmpty; + } + + public Map getB2_mapFloatFloat() { + return b2_mapFloatFloat; + } + + public void setB2_mapFloatFloat(Map b2_mapFloatFloat) { + this.b2_mapFloatFloat = b2_mapFloatFloat; + } + + public Set getB2_setArrayDouble() { + return b2_setArrayDouble; + } + + public void setB2_setArrayDouble(Set b2_setArrayDouble) { + this.b2_setArrayDouble = b2_setArrayDouble; + } + + public List getB2_listPoint() { + return b2_listPoint; + } + + public void setB2_listPoint(List b2_listPoint) { + this.b2_listPoint = b2_listPoint; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + ExampleConfigurationB2 that = (ExampleConfigurationB2) o; + return b2_transientInt == that.b2_transientInt && + b2_ignoredInt == that.b2_ignoredInt && + b2_primChar == that.b2_primChar && + Objects.equals(b2_ignoredString, that.b2_ignoredString) && + Objects.equals(b2_ignoredListString, that.b2_ignoredListString) && + Objects.equals(b2_refBool, that.b2_refBool) && + Objects.equals(b2_bigInteger, that.b2_bigInteger) && + Objects.equals(b2_listShort, that.b2_listShort) && + Arrays.equals(b2_arrayInteger, that.b2_arrayInteger) && + Objects.equals(b2_setLong, that.b2_setLong) && + Arrays.equals(b2_arrayEmpty, that.b2_arrayEmpty) && + Objects.equals(b2_mapFloatFloat, that.b2_mapFloatFloat) && + collectionOfArraysDeepEquals(b2_setArrayDouble, that.b2_setArrayDouble, HashSet::new) && + Objects.equals(b2_listPoint, that.b2_listPoint); + } + + @Override + public int hashCode() { + return 0; + } +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationNulls.java b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationNulls.java new file mode 100644 index 0000000..b21d083 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationNulls.java @@ -0,0 +1,184 @@ +package de.exlll.configlib.configurations; + +import de.exlll.configlib.Configuration; + +import java.awt.Point; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.util.*; + +@Configuration +public final class ExampleConfigurationNulls { + /* FIELDS SET TO NULL */ + private Integer nullInteger; + private String nullString; + private ExampleEnum nullEnm; + private ExampleConfigurationB1 nullB1; + private List nullList; + private Double[] nullArray; + private Set nullSet; + private Map nullMap; + private Point nullPoint; + + /* NULL VALUES IN CONTAINER */ + private List listNullString; + private Double[] arrayNullDouble; + private Set setNullInteger; + private Map mapNullEnmKey; + private Map mapNullBigIntegerValue; + + public Integer getNullInteger() { + return nullInteger; + } + + public void setNullInteger(Integer nullInteger) { + this.nullInteger = nullInteger; + } + + public String getNullString() { + return nullString; + } + + public void setNullString(String nullString) { + this.nullString = nullString; + } + + public ExampleEnum getNullEnm() { + return nullEnm; + } + + public void setNullEnm(ExampleEnum nullEnm) { + this.nullEnm = nullEnm; + } + + public ExampleConfigurationB1 getNullB1() { + return nullB1; + } + + public void setNullB1(ExampleConfigurationB1 nullB1) { + this.nullB1 = nullB1; + } + + public List getNullList() { + return nullList; + } + + public void setNullList(List nullList) { + this.nullList = nullList; + } + + public Double[] getNullArray() { + return nullArray; + } + + public void setNullArray(Double[] nullArray) { + this.nullArray = nullArray; + } + + public Set getNullSet() { + return nullSet; + } + + public void setNullSet(Set nullSet) { + this.nullSet = nullSet; + } + + public Map getNullMap() { + return nullMap; + } + + public void setNullMap(Map nullMap) { + this.nullMap = nullMap; + } + + public Point getNullPoint() { + return nullPoint; + } + + public void setNullPoint(Point nullPoint) { + this.nullPoint = nullPoint; + } + + public List getListNullString() { + return listNullString; + } + + public void setListNullString(List listNullString) { + this.listNullString = listNullString; + } + + public Double[] getArrayNullDouble() { + return arrayNullDouble; + } + + public void setArrayNullDouble(Double[] arrayNullDouble) { + this.arrayNullDouble = arrayNullDouble; + } + + public Set getSetNullInteger() { + return setNullInteger; + } + + public void setSetNullInteger(Set setNullInteger) { + this.setNullInteger = setNullInteger; + } + + public Map getMapNullEnmKey() { + return mapNullEnmKey; + } + + public void setMapNullEnmKey(Map mapNullEnmKey) { + this.mapNullEnmKey = mapNullEnmKey; + } + + public Map getMapNullBigIntegerValue() { + return mapNullBigIntegerValue; + } + + public void setMapNullBigIntegerValue(Map mapNullBigIntegerValue) { + this.mapNullBigIntegerValue = mapNullBigIntegerValue; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ExampleConfigurationNulls that = (ExampleConfigurationNulls) o; + return Objects.equals(nullInteger, that.nullInteger) && + Objects.equals(nullString, that.nullString) && + nullEnm == that.nullEnm && + Objects.equals(nullB1, that.nullB1) && + Objects.equals(nullList, that.nullList) && + Arrays.equals(nullArray, that.nullArray) && + Objects.equals(nullSet, that.nullSet) && + Objects.equals(nullMap, that.nullMap) && + Objects.equals(nullPoint, that.nullPoint) && + Objects.equals(listNullString, that.listNullString) && + Arrays.equals(arrayNullDouble, that.arrayNullDouble) && + Objects.equals(setNullInteger, that.setNullInteger) && + Objects.equals(mapNullEnmKey, that.mapNullEnmKey) && + Objects.equals(mapNullBigIntegerValue, that.mapNullBigIntegerValue); + } + + @Override + public int hashCode() { + int result = Objects.hash( + nullInteger, + nullString, + nullEnm, + nullB1, + nullList, + nullSet, + nullMap, + nullPoint, + listNullString, + setNullInteger, + mapNullEnmKey, + mapNullBigIntegerValue + ); + result = 31 * result + Arrays.hashCode(nullArray); + result = 31 * result + Arrays.hashCode(arrayNullDouble); + return result; + } +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationsSerialized.java b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationsSerialized.java new file mode 100644 index 0000000..e3e1ca4 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleConfigurationsSerialized.java @@ -0,0 +1,419 @@ +package de.exlll.configlib.configurations; + +import de.exlll.configlib.TestUtils; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static de.exlll.configlib.TestUtils.*; +import static java.util.Arrays.asList; + +public final class ExampleConfigurationsSerialized { + public static final Map EXAMPLE_CONFIGURATION_B1_1 = entriesAsMap( + entry("b1_primBool", true), + entry("b1_refChar", "b"), + entry("b1_string", "c"), + entry("b1_listByte", List.of(1L, 2L, 3L)), + entry("b1_arrayShort", List.of(1L, 2L, 3L)), + entry("b1_setInteger", asList(1L, 2L, 3L)), + entry("b1_listEmpty", Collections.emptyList()), + entry("b1_mapLongLong", asMap(1L, 2L, 3L, 4L)), + entry("b1_listListByte", List.of( + List.of(1L, 2L, 3L), + List.of(2L, 3L, 4L) + )), + entry("b1_point", "1:2") + ); + public static final Map EXAMPLE_CONFIGURATION_B1_2 = entriesAsMap( + entry("b1_primBool", false), + entry("b1_refChar", "c"), + entry("b1_string", "d"), + entry("b1_listByte", List.of(2L, 3L, 4L)), + entry("b1_arrayShort", List.of(2L, 3L, 4L)), + entry("b1_setInteger", asList(2L, 3L, 4L)), + entry("b1_listEmpty", Collections.emptyList()), + entry("b1_mapLongLong", asMap(2L, 3L, 4L, 5L)), + entry("b1_listListByte", List.of( + List.of(2L, 3L, 4L), + List.of(3L, 4L, 5L) + )), + entry("b1_point", "1:3") + ); + public static final Map EXAMPLE_CONFIGURATION_B2_1 = extend(EXAMPLE_CONFIGURATION_B1_1, + entry("b2_primChar", "a"), + entry("b2_refBool", true), + entry("b2_bigInteger", "1"), + entry("b2_listShort", List.of(2L, 3L, 4L)), + entry("b2_arrayInteger", List.of(2L, 3L, 4L)), + entry("b2_setLong", asList(2L, 3L, 4L)), + entry("b2_arrayEmpty", Collections.emptyList()), + entry("b2_mapFloatFloat", asMap(1d, 2d, 3d, 4d)), + entry("b2_setArrayDouble", asList( + List.of(1d, 2d, 3d, 4d), + List.of(5d, 6d, 7d, 8d) + )), + entry("b2_listPoint", List.of("1:2", "3:4")) + ); + public static final Map EXAMPLE_CONFIGURATION_B2_2 = extend(EXAMPLE_CONFIGURATION_B1_2, + entry("b2_primChar", "b"), + entry("b2_refBool", false), + entry("b2_bigInteger", "2"), + entry("b2_listShort", List.of(3L, 4L, 5L)), + entry("b2_arrayInteger", List.of(3L, 4L, 5L)), + entry("b2_setLong", asList(3L, 4L, 5L)), + entry("b2_arrayEmpty", Collections.emptyList()), + entry("b2_mapFloatFloat", asMap(2d, 3d, 4d, 5d)), + entry("b2_setArrayDouble", asList( + List.of(2d, 3d, 4d, 5d), + List.of(6d, 7d, 8d, 9d) + )), + entry("b2_listPoint", List.of("2:3", "4:5")) + ); + public static final Map EXAMPLE_CONFIGURATION_A1 = TestUtils.entriesAsMap( + entry("a1_primBool", true), + entry("a1_primChar", "a"), + entry("a1_primByte", 1L), + entry("a1_primShort", 3L), + entry("a1_primInt", 5L), + entry("a1_primLong", 7L), + entry("a1_primFloat", 9d), + entry("a1_primDouble", 11d), + entry("a1_refBool", true), + entry("a1_refChar", "c"), + entry("a1_refByte", 13L), + entry("a1_refShort", 15L), + entry("a1_refInt", 17L), + entry("a1_refLong", 19L), + entry("a1_refFloat", 21d), + entry("a1_refDouble", 23d), + entry("a1_string", "a"), + entry("a1_bigInteger", "1"), + entry("a1_bigDecimal", "1"), + entry("a1_localDate", "2000-01-01"), + entry("a1_localTime", "00:01"), + entry("a1_localDateTime", "2000-01-01T00:00"), + entry("a1_Enm", "A"), + entry("a1_b1", EXAMPLE_CONFIGURATION_B1_1), + entry("a1_b2", EXAMPLE_CONFIGURATION_B2_1), + entry("a1_listBoolean", List.of(true, false, true)), + entry("a1_listChar", List.of("a", "b", "c")), + entry("a1_listByte", List.of(1L, 2L, 3L)), + entry("a1_listShort", List.of(1L, 2L, 3L)), + entry("a1_listInteger", List.of(1L, 2L, 3L)), + entry("a1_listLong", List.of(1L, 2L, 3L)), + entry("a1_listFloat", List.of(1d, 2d, 3d)), + entry("a1_listDouble", List.of(1d, 2d, 3d)), + entry("a1_listString", List.of("1", "2", "3")), + entry("a1_listBigInteger", List.of("1", "2", "3")), + entry("a1_listBigDecimal", List.of("1", "2", "3")), + entry("a1_listLocalDate", List.of("2000-01-01", "2000-01-02", "2000-01-03")), + entry("a1_listLocalTime", List.of("00:01", "00:02", "00:03")), + entry("a1_listLocalDateTime", List.of("2000-01-01T00:00", "2000-01-02T00:00", "2000-01-03T00:00")), + entry("a1_listEnm", List.of("A", "B", "C")), + entry("a1_listB1", List.of(EXAMPLE_CONFIGURATION_B1_1)), + entry("a1_listB2", List.of(EXAMPLE_CONFIGURATION_B2_1)), + entry("a1_arrayPrimBoolean", List.of(true, false, true)), + entry("a1_arrayPrimChar", List.of("a", "b", "c")), + entry("a1_arrayPrimByte", List.of(1L, 2L, 3L)), + entry("a1_arrayPrimShort", List.of(1L, 2L, 3L)), + entry("a1_arrayPrimInteger", List.of(1L, 2L, 3L)), + entry("a1_arrayPrimLong", List.of(1L, 2L, 3L)), + entry("a1_arrayPrimFloat", List.of(1d, 2d, 3d)), + entry("a1_arrayPrimDouble", List.of(1d, 2d, 3d)), + entry("a1_arrayBoolean", List.of(true, false, true)), + entry("a1_arrayChar", List.of("a", "b", "c")), + entry("a1_arrayByte", List.of(1L, 2L, 3L)), + entry("a1_arrayShort", List.of(1L, 2L, 3L)), + entry("a1_arrayInteger", List.of(1L, 2L, 3L)), + entry("a1_arrayLong", List.of(1L, 2L, 3L)), + entry("a1_arrayFloat", List.of(1d, 2d, 3d)), + entry("a1_arrayDouble", List.of(1d, 2d, 3d)), + entry("a1_arrayString", List.of("1", "2", "3")), + entry("a1_arrayBigInteger", List.of("1", "2", "3")), + entry("a1_arrayBigDecimal", List.of("1", "2", "3")), + entry("a1_arrayLocalDate", List.of("2000-01-01", "2000-01-02", "2000-01-03")), + entry("a1_arrayLocalTime", List.of("00:01", "00:02", "00:03")), + entry("a1_arrayLocalDateTime", List.of("2000-01-01T00:00", "2000-01-02T00:00", "2000-01-03T00:00")), + entry("a1_arrayEnm", List.of("A", "B", "C")), + entry("a1_arrayB1", List.of(EXAMPLE_CONFIGURATION_B1_1)), + entry("a1_arrayB2", List.of(EXAMPLE_CONFIGURATION_B2_1)), + entry("a1_setBoolean", List.of(true)), + entry("a1_setChar", asList("a", "b", "c")), + entry("a1_setByte", asList(1L, 2L, 3L)), + entry("a1_setShort", asList(1L, 2L, 3L)), + entry("a1_setInteger", asList(1L, 2L, 3L)), + entry("a1_setLong", asList(1L, 2L, 3L)), + entry("a1_setFloat", asList(1d, 2d, 3d)), + entry("a1_setDouble", asList(1d, 2d, 3d)), + entry("a1_setString", asList("1", "2", "3")), + entry("a1_setBigInteger", asList("1", "2", "3")), + entry("a1_setBigDecimal", asList("1", "2", "3")), + entry("a1_setLocalDate", asList("2000-01-01", "2000-01-02", "2000-01-03")), + entry("a1_setLocalTime", asList("00:01", "00:02", "00:03")), + entry("a1_setLocalDateTime", asList("2000-01-01T00:00", "2000-01-02T00:00", "2000-01-03T00:00")), + entry("a1_setEnm", asList("A", "B", "C")), + entry("a1_setB1", List.of(EXAMPLE_CONFIGURATION_B1_1)), + entry("a1_setB2", List.of(EXAMPLE_CONFIGURATION_B2_1)), + entry("a1_mapBooleanBoolean", asMap(true, true, false, false)), + entry("a1_mapCharChar", asMap("a", "b", "c", "d")), + entry("a1_mapByteByte", asMap(1L, 2L, 3L, 4L)), + entry("a1_mapShortShort", asMap(1L, 2L, 3L, 4L)), + entry("a1_mapIntegerInteger", asMap(1L, 2L, 3L, 4L)), + entry("a1_mapLongLong", asMap(1L, 2L, 3L, 4L)), + entry("a1_mapFloatFloat", asMap(1d, 2d, 3d, 4d)), + entry("a1_mapDoubleDouble", asMap(1d, 2d, 3d, 4d)), + entry("a1_mapStringString", asMap("1", "2", "3", "4")), + entry("a1_mapBigIntegerBigInteger", asMap("1", "2", "3", "4")), + entry("a1_mapBigDecimalBigDecimal", asMap("1", "2", "3", "4")), + entry("a1_mapLocalDateLocalDate", asMap("2000-01-01", "2000-01-02", "2000-01-03", "2000-01-04")), + entry("a1_mapLocalTimeLocalTime", asMap("00:01", "00:02", "00:03", "00:04")), + entry("a1_mapLocalDateTimeLocalDateTime", asMap("2000-01-01T00:00", "2000-01-02T00:00", "2000-01-03T00:00", "2000-01-04T00:00")), + entry("a1_mapEnmEnm", asMap("A", "B", "C", "D")), + entry("a1_mapIntegerB1", asMap(1L, EXAMPLE_CONFIGURATION_B1_1, 2L, EXAMPLE_CONFIGURATION_B1_2)), + entry("a1_mapEnmB2", asMap("A", EXAMPLE_CONFIGURATION_B2_1, "B", EXAMPLE_CONFIGURATION_B2_2)), + entry("a1_listEmpty", Collections.emptyList()), + entry("a1_arrayEmpty", Collections.emptyList()), + entry("a1_setEmpty", Collections.emptyList()), + entry("a1_mapEmpty", Collections.emptyMap()), + entry("a1_listListByte", List.of(List.of(), List.of(1L), List.of(1L, 2L))), + entry("a1_listArrayFloat", List.of(List.of(), List.of(1d), List.of(1d, 2d))), + entry("a1_listSetString", List.of(List.of(), List.of("1"), asList("1", "2"))), + entry("a1_listMapEnmLocalDate", List.of(asMap(), asMap("A", "2000-01-01"), asMap("A", "2000-01-01", "B", "2000-01-02"))), + entry("a1_setSetShort", asList(List.of(), List.of(1L), asList(1L, 2L))), + entry("a1_setArrayDouble", asList(List.of(), List.of(1d), List.of(1d, 2d))), + entry("a1_setListString", asList(List.of(), List.of("1"), List.of("1", "2"))), + entry("a1_setMapEnmLocalTime", asList(asMap(), asMap("A", "00:01"), asMap("A", "00:01", "B", "00:02"))), + entry("a1_mapIntegerMapLongBoolean", asMap(1L, asMap(), 2L, asMap(1L, true), 3L, asMap(1L, true, 2L, false))), + entry("a1_mapStringListB1", asMap("1", List.of(), "2", List.of(EXAMPLE_CONFIGURATION_B1_1), "3", List.of(EXAMPLE_CONFIGURATION_B1_1, EXAMPLE_CONFIGURATION_B1_2))), + entry("a1_mapBigIntegerArrayBigDecimal", asMap("1", List.of(), "2", List.of("1"), "3", List.of("1", "2"))), + entry("a1_mapEnmSetB2", asMap("A", List.of(), "B", List.of(EXAMPLE_CONFIGURATION_B2_1), "C", asList(EXAMPLE_CONFIGURATION_B2_1, EXAMPLE_CONFIGURATION_B2_2))), + entry("a1_mapIntegerListMapShortSetB2", asMap(1L, List.of(), 2L, List.of(asMap()), 3L, List.of(asMap(1L, List.of(), 2L, List.of(EXAMPLE_CONFIGURATION_B2_1))), 4L, List.of(asMap(1L, List.of(), 2L, List.of(EXAMPLE_CONFIGURATION_B2_1)), asMap()))), + entry("a1_arrayArrayPrimBoolean", List.of(List.of(), List.of(true), List.of(true, false))), + entry("a1_arrayArrayPrimChar", List.of(List.of(), List.of("a"), List.of("a", "b"))), + entry("a1_arrayArrayPrimByte", List.of(List.of(), List.of(1L), List.of(1L, 2L))), + entry("a1_arrayArrayPrimShort", List.of(List.of(), List.of(1L), List.of(1L, 2L))), + entry("a1_arrayArrayPrimInteger", List.of(List.of(), List.of(1L), List.of(1L, 2L))), + entry("a1_arrayArrayPrimLong", List.of(List.of(), List.of(1L), List.of(1L, 2L))), + entry("a1_arrayArrayPrimFloat", List.of(List.of(), List.of(1d), List.of(1d, 2d))), + entry("a1_arrayArrayPrimDouble", List.of(List.of(), List.of(1d), List.of(1d, 2d))), + entry("a1_arrayArrayBoolean", List.of(List.of(), List.of(true), List.of(true, false))), + entry("a1_arrayArrayChar", List.of(List.of(), List.of("a"), List.of("a", "b"))), + entry("a1_arrayArrayByte", List.of(List.of(), List.of(1L), List.of(1L, 2L))), + entry("a1_arrayArrayShort", List.of(List.of(), List.of(1L), List.of(1L, 2L))), + entry("a1_arrayArrayInteger", List.of(List.of(), List.of(1L), List.of(1L, 2L))), + entry("a1_arrayArrayLong", List.of(List.of(), List.of(1L), List.of(1L, 2L))), + entry("a1_arrayArrayFloat", List.of(List.of(), List.of(1d), List.of(1d, 2d))), + entry("a1_arrayArrayDouble", List.of(List.of(), List.of(1d), List.of(1d, 2d))), + entry("a1_arrayArrayString", List.of(List.of(), List.of("a"), List.of("a", "b"))), + entry("a1_arrayArrayBigInteger", List.of(List.of(), List.of("1"), List.of("1", "2"))), + entry("a1_arrayArrayBigDecimal", List.of(List.of(), List.of("1"), List.of("1", "2"))), + entry("a1_arrayArrayLocalDate", List.of(List.of(), List.of("2000-01-01"), List.of("2000-01-01", "2000-01-02"))), + entry("a1_arrayArrayLocalTime", List.of(List.of(), List.of("00:01"), List.of("00:01", "00:02"))), + entry("a1_arrayArrayLocalDateTime", List.of(List.of(), List.of("2000-01-01T00:00"), List.of("2000-01-01T00:00", "2000-01-02T00:00"))), + entry("a1_arrayArrayEnm", List.of(List.of(), List.of("A"), List.of("A", "B"))), + entry("a1_arrayArrayB1", List.of(List.of(), List.of(EXAMPLE_CONFIGURATION_B1_1))), + entry("a1_arrayArrayB2", List.of(List.of(), List.of(EXAMPLE_CONFIGURATION_B2_1))), + entry("a1_point", "0:1"), + entry("a1_listPoint", List.of("0:1", "0:2", "0:3")), + entry("a1_arrayPoint", List.of("0:1", "0:2", "0:3")), + entry("a1_setPoint", asList("0:1", "0:2", "0:3")), + entry("a1_mapEnmListPoint", asMap("A", List.of(), "B", List.of("0:1"), "C", List.of("0:1", "0:2"))) + ); + public static final Map EXAMPLE_CONFIGURATION_A2 = extend(EXAMPLE_CONFIGURATION_A1, + entry("a2_primBool", true), + entry("a2_primChar", "b"), + entry("a2_primByte", 2L), + entry("a2_primShort", 4L), + entry("a2_primInt", 6L), + entry("a2_primLong", 8L), + entry("a2_primFloat", 10d), + entry("a2_primDouble", 12d), + entry("a2_refBool", true), + entry("a2_refChar", "d"), + entry("a2_refByte", 14L), + entry("a2_refShort", 16L), + entry("a2_refInt", 18L), + entry("a2_refLong", 20L), + entry("a2_refFloat", 22d), + entry("a2_refDouble", 24d), + entry("a2_string", "b"), + entry("a2_bigInteger", "2"), + entry("a2_bigDecimal", "2"), + entry("a2_localDate", "2000-01-02"), + entry("a2_localTime", "00:02"), + entry("a2_localDateTime", "2000-01-02T00:00"), + entry("a2_Enm", "B"), + entry("a2_b1", EXAMPLE_CONFIGURATION_B1_2), + entry("a2_b2", EXAMPLE_CONFIGURATION_B2_2), + entry("a2_listBoolean", List.of(false, true, false)), + entry("a2_listChar", List.of("d", "e", "f")), + entry("a2_listByte", List.of(2L, 3L, 4L)), + entry("a2_listShort", List.of(2L, 3L, 4L)), + entry("a2_listInteger", List.of(2L, 3L, 4L)), + entry("a2_listLong", List.of(2L, 3L, 4L)), + entry("a2_listFloat", List.of(2d, 3d, 4d)), + entry("a2_listDouble", List.of(2d, 3d, 4d)), + entry("a2_listString", List.of("2", "3", "4")), + entry("a2_listBigInteger", List.of("2", "3", "4")), + entry("a2_listBigDecimal", List.of("2", "3", "4")), + entry("a2_listLocalDate", List.of("2000-01-02", "2000-01-03", "2000-01-04")), + entry("a2_listLocalTime", List.of("00:02", "00:03", "00:04")), + entry("a2_listLocalDateTime", List.of("2000-01-02T00:00", "2000-01-03T00:00", "2000-01-04T00:00")), + entry("a2_listEnm", List.of("B", "C", "D")), + entry("a2_listB1", List.of(EXAMPLE_CONFIGURATION_B1_1, EXAMPLE_CONFIGURATION_B1_2)), + entry("a2_listB2", List.of(EXAMPLE_CONFIGURATION_B2_1, EXAMPLE_CONFIGURATION_B2_2)), + entry("a2_arrayPrimBoolean", List.of(false, true, false)), + entry("a2_arrayPrimChar", List.of("d", "e", "f")), + entry("a2_arrayPrimByte", List.of(2L, 3L, 4L)), + entry("a2_arrayPrimShort", List.of(2L, 3L, 4L)), + entry("a2_arrayPrimInteger", List.of(2L, 3L, 4L)), + entry("a2_arrayPrimLong", List.of(2L, 3L, 4L)), + entry("a2_arrayPrimFloat", List.of(2d, 3d, 4d)), + entry("a2_arrayPrimDouble", List.of(2d, 3d, 4d)), + entry("a2_arrayBoolean", List.of(false, true, false)), + entry("a2_arrayChar", List.of("d", "e", "f")), + entry("a2_arrayByte", List.of(2L, 3L, 4L)), + entry("a2_arrayShort", List.of(2L, 3L, 4L)), + entry("a2_arrayInteger", List.of(2L, 3L, 4L)), + entry("a2_arrayLong", List.of(2L, 3L, 4L)), + entry("a2_arrayFloat", List.of(2d, 3d, 4d)), + entry("a2_arrayDouble", List.of(2d, 3d, 4d)), + entry("a2_arrayString", List.of("2", "3", "4")), + entry("a2_arrayBigInteger", List.of("2", "3", "4")), + entry("a2_arrayBigDecimal", List.of("2", "3", "4")), + entry("a2_arrayLocalDate", List.of("2000-01-02", "2000-01-03", "2000-01-04")), + entry("a2_arrayLocalTime", List.of("00:02", "00:03", "00:04")), + entry("a2_arrayLocalDateTime", List.of("2000-01-02T00:00", "2000-01-03T00:00", "2000-01-04T00:00")), + entry("a2_arrayEnm", List.of("B", "C", "D")), + entry("a2_arrayB1", List.of(EXAMPLE_CONFIGURATION_B1_1, EXAMPLE_CONFIGURATION_B1_2)), + entry("a2_arrayB2", List.of(EXAMPLE_CONFIGURATION_B2_1, EXAMPLE_CONFIGURATION_B2_2)), + entry("a2_setBoolean", List.of(false)), + entry("a2_setChar", asList("d", "e", "f")), + entry("a2_setByte", asList(2L, 3L, 4L)), + entry("a2_setShort", asList(2L, 3L, 4L)), + entry("a2_setInteger", asList(2L, 3L, 4L)), + entry("a2_setLong", asList(2L, 3L, 4L)), + entry("a2_setFloat", asList(2d, 3d, 4d)), + entry("a2_setDouble", asList(2d, 3d, 4d)), + entry("a2_setString", asList("2", "3", "4")), + entry("a2_setBigInteger", asList("2", "3", "4")), + entry("a2_setBigDecimal", asList("2", "3", "4")), + entry("a2_setLocalDate", asList("2000-01-02", "2000-01-03", "2000-01-04")), + entry("a2_setLocalTime", asList("00:02", "00:03", "00:04")), + entry("a2_setLocalDateTime", asList("2000-01-02T00:00", "2000-01-03T00:00", "2000-01-04T00:00")), + entry("a2_setEnm", asList("B", "C", "D")), + entry("a2_setB1", asList(EXAMPLE_CONFIGURATION_B1_1, EXAMPLE_CONFIGURATION_B1_2)), + entry("a2_setB2", asList(EXAMPLE_CONFIGURATION_B2_1, EXAMPLE_CONFIGURATION_B2_2)), + entry("a2_mapBooleanBoolean", asMap(true, true, false, false)), + entry("a2_mapCharChar", asMap("b", "c", "d", "e")), + entry("a2_mapByteByte", asMap(2L, 3L, 4L, 5L)), + entry("a2_mapShortShort", asMap(2L, 3L, 4L, 5L)), + entry("a2_mapIntegerInteger", asMap(2L, 3L, 4L, 5L)), + entry("a2_mapLongLong", asMap(2L, 3L, 4L, 5L)), + entry("a2_mapFloatFloat", asMap(2d, 3d, 4d, 5d)), + entry("a2_mapDoubleDouble", asMap(2d, 3d, 4d, 5d)), + entry("a2_mapStringString", asMap("2", "3", "4", "5")), + entry("a2_mapBigIntegerBigInteger", asMap("2", "3", "4", "5")), + entry("a2_mapBigDecimalBigDecimal", asMap("2", "3", "4", "5")), + entry("a2_mapLocalDateLocalDate", asMap("2000-01-02", "2000-01-03", "2000-01-04", "2000-01-05")), + entry("a2_mapLocalTimeLocalTime", asMap("00:02", "00:03", "00:04", "00:05")), + entry("a2_mapLocalDateTimeLocalDateTime", asMap("2000-01-02T00:00", "2000-01-03T00:00", "2000-01-04T00:00", "2000-01-05T00:00")), + entry("a2_mapEnmEnm", asMap("B", "C", "D", "E")), + entry("a2_mapIntegerB1", asMap(2L, EXAMPLE_CONFIGURATION_B1_1, 3L, EXAMPLE_CONFIGURATION_B1_2)), + entry("a2_mapEnmB2", asMap("B", EXAMPLE_CONFIGURATION_B2_1, "C", EXAMPLE_CONFIGURATION_B2_2)), + entry("a2_listEmpty", Collections.emptyList()), + entry("a2_arrayEmpty", Collections.emptyList()), + entry("a2_setEmpty", Collections.emptyList()), + entry("a2_mapEmpty", Collections.emptyMap()), + entry("a2_listListByte", List.of(List.of(1L), List.of(1L, 2L), List.of(1L, 2L, 3L))), + entry("a2_listArrayFloat", List.of(List.of(1d), List.of(1d, 2d), List.of(1d, 2d, 3d))), + entry("a2_listSetString", List.of(List.of("1"), asList("1", "2"), asList("1", "2", "3"))), + entry("a2_listMapEnmLocalDate", List.of(asMap("A", "2000-01-01"), asMap("A", "2000-01-01", "B", "2000-01-02"), asMap("A", "2000-01-01", "B", "2000-01-02", "C", "2000-01-03"))), + entry("a2_setSetShort", asList(List.of(1L), asList(1L, 2L), asList(1L, 2L, 3L))), + entry("a2_setArrayDouble", asList(List.of(1d), List.of(1d, 2d), List.of(1d, 2d, 3d))), + entry("a2_setListString", asList(List.of("1"), List.of("1", "2"), List.of("1", "2", "3"))), + entry("a2_setMapEnmLocalTime", asList(asMap("A", "00:01"), asMap("A", "00:01", "B", "00:02"), asMap("A", "00:01", "B", "00:02", "C", "00:03"))), + entry("a2_mapIntegerMapLongBoolean", asMap(2L, asMap(1L, true), 3L, asMap(1L, true, 2L, false), 4L, asMap(1L, true, 2L, false, 3L, true))), + entry("a2_mapStringListB1", asMap("2", List.of(EXAMPLE_CONFIGURATION_B1_1), "3", List.of(EXAMPLE_CONFIGURATION_B1_1, EXAMPLE_CONFIGURATION_B1_2), "4", List.of(EXAMPLE_CONFIGURATION_B1_1, EXAMPLE_CONFIGURATION_B1_2, EXAMPLE_CONFIGURATION_B1_1))), + entry("a2_mapBigIntegerArrayBigDecimal", asMap("2", List.of("1"), "3", List.of("1", "2"), "4", List.of("1", "2", "3"))), + entry("a2_mapEnmSetB2", asMap("B", List.of(EXAMPLE_CONFIGURATION_B2_1), "C", asList(EXAMPLE_CONFIGURATION_B2_1, EXAMPLE_CONFIGURATION_B2_2), "D", List.of())), + entry("a2_mapIntegerListMapShortSetB2", asMap(2L, List.of(asMap()), 3L, List.of(asMap(1L, List.of(), 2L, List.of(EXAMPLE_CONFIGURATION_B2_1))), 4L, List.of(asMap(1L, List.of(), 2L, List.of(EXAMPLE_CONFIGURATION_B2_1)), asMap()), 5L, List.of(asMap(1L, List.of(), 2L, List.of(EXAMPLE_CONFIGURATION_B2_1)), asMap(1L, List.of())))), + entry("a2_arrayArrayPrimBoolean", List.of(List.of(true), List.of(true, false), List.of(true, false, true))), + entry("a2_arrayArrayPrimChar", List.of(List.of("a"), List.of("a", "b"), List.of("a", "b", "c"))), + entry("a2_arrayArrayPrimByte", List.of(List.of(1L), List.of(1L, 2L), List.of(1L, 2L, 3L))), + entry("a2_arrayArrayPrimShort", List.of(List.of(1L), List.of(1L, 2L), List.of(1L, 2L, 3L))), + entry("a2_arrayArrayPrimInteger", List.of(List.of(1L), List.of(1L, 2L), List.of(1L, 2L, 3L))), + entry("a2_arrayArrayPrimLong", List.of(List.of(1L), List.of(1L, 2L), List.of(1L, 2L, 3L))), + entry("a2_arrayArrayPrimFloat", List.of(List.of(1d), List.of(1d, 2d), List.of(1d, 2d, 3d))), + entry("a2_arrayArrayPrimDouble", List.of(List.of(1d), List.of(1d, 2d), List.of(1d, 2d, 3d))), + entry("a2_arrayArrayBoolean", List.of(List.of(true), List.of(true, false), List.of(true, false, true))), + entry("a2_arrayArrayChar", List.of(List.of("a"), List.of("a", "b"), List.of("a", "b", "c"))), + entry("a2_arrayArrayByte", List.of(List.of(1L), List.of(1L, 2L), List.of(1L, 2L, 3L))), + entry("a2_arrayArrayShort", List.of(List.of(1L), List.of(1L, 2L), List.of(1L, 2L, 3L))), + entry("a2_arrayArrayInteger", List.of(List.of(1L), List.of(1L, 2L), List.of(1L, 2L, 3L))), + entry("a2_arrayArrayLong", List.of(List.of(1L), List.of(1L, 2L), List.of(1L, 2L, 3L))), + entry("a2_arrayArrayFloat", List.of(List.of(1d), List.of(1d, 2d), List.of(1d, 2d, 3d))), + entry("a2_arrayArrayDouble", List.of(List.of(1d), List.of(1d, 2d), List.of(1d, 2d, 3d))), + entry("a2_arrayArrayString", List.of(List.of("a"), List.of("a", "b"), List.of("a", "b", "c"))), + entry("a2_arrayArrayBigInteger", List.of(List.of("1"), List.of("1", "2"), List.of("1", "2", "3"))), + entry("a2_arrayArrayBigDecimal", List.of(List.of("1"), List.of("1", "2"), List.of("1", "2", "3"))), + entry("a2_arrayArrayLocalDate", List.of(List.of("2000-01-01"), List.of("2000-01-01", "2000-01-02"), List.of("2000-01-01", "2000-01-02", "2000-01-03"))), + entry("a2_arrayArrayLocalTime", List.of(List.of("00:01"), List.of("00:01", "00:02"), List.of("00:01", "00:02", "00:03"))), + entry("a2_arrayArrayLocalDateTime", List.of(List.of("2000-01-01T00:00"), List.of("2000-01-01T00:00", "2000-01-02T00:00"), List.of("2000-01-01T00:00", "2000-01-02T00:00", "2000-01-03T00:00"))), + entry("a2_arrayArrayEnm", List.of(List.of("A"), List.of("A", "B"), List.of("A", "B", "C"))), + entry("a2_arrayArrayB1", List.of(List.of(EXAMPLE_CONFIGURATION_B1_1), List.of(EXAMPLE_CONFIGURATION_B1_2))), + entry("a2_arrayArrayB2", List.of(List.of(EXAMPLE_CONFIGURATION_B2_1), List.of(EXAMPLE_CONFIGURATION_B2_2))), + entry("a2_point", "0:2"), + entry("a2_listPoint", List.of("0:2", "0:3", "0:4")), + entry("a2_arrayPoint", List.of("0:2", "0:3", "0:4")), + entry("a2_setPoint", asList("0:2", "0:3", "0:4")), + entry("a2_mapEnmListPoint", asMap("B", List.of("0:1"), "C", List.of("0:1", "0:2"), "D", List.of("0:1", "0:2", "0:3"))) + ); + public static final Map EXAMPLE_CONFIGURATION_NULLS_WITH_1 = entriesAsMap( + entry("nullInteger", null), + entry("nullString", null), + entry("nullEnm", null), + entry("nullB1", null), + entry("nullList", null), + entry("nullArray", null), + entry("nullSet", null), + entry("nullMap", null), + entry("nullPoint", null), + entry("listNullString", asList(null, "a", null)), + entry("arrayNullDouble", asList(null, 1d, null)), + entry("setNullInteger", Collections.singletonList((Long) null)), + entry("mapNullEnmKey", entriesAsMap(entry(null, "1"))), + entry("mapNullBigIntegerValue", entriesAsMap(entry("A", null))) + ); + public static final Map EXAMPLE_CONFIGURATION_NULLS_WITH_2 = entriesAsMap( + entry("nullInteger", null), + entry("nullString", null), + entry("nullEnm", null), + entry("nullB1", null), + entry("nullList", null), + entry("nullArray", null), + entry("nullSet", null), + entry("nullMap", null), + entry("nullPoint", null), + entry("listNullString", asList("b", null, "c")), + entry("arrayNullDouble", asList(2d, null, 3d)), + entry("setNullInteger", asList(null, 1L)), + entry("mapNullEnmKey", entriesAsMap(entry(null, "1"), entry("A", "2"))), + entry("mapNullBigIntegerValue", entriesAsMap(entry("A", null), entry("B", "1"))) + ); + public static final Map EXAMPLE_CONFIGURATION_NULLS_WITHOUT_1 = entriesAsMap( + entry("listNullString", List.of("a")), + entry("arrayNullDouble", List.of(1d)), + entry("setNullInteger", List.of()), + entry("mapNullEnmKey", entriesAsMap()), + entry("mapNullBigIntegerValue", entriesAsMap()) + ); + public static final Map EXAMPLE_CONFIGURATION_NULLS_WITHOUT_2 = entriesAsMap( + entry("listNullString", asList("b", "c")), + entry("arrayNullDouble", asList(2d, 3d)), + entry("setNullInteger", List.of(1L)), + entry("mapNullEnmKey", entriesAsMap(entry("A", "2"))), + entry("mapNullBigIntegerValue", entriesAsMap(entry("B", "1"))) + ); +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleEnum.java b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleEnum.java new file mode 100644 index 0000000..27d5bc3 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleEnum.java @@ -0,0 +1,5 @@ +package de.exlll.configlib.configurations; + +public enum ExampleEnum { + A, B, C, D, E +} diff --git a/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleInitializer.java b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleInitializer.java new file mode 100644 index 0000000..a306cb5 --- /dev/null +++ b/configlib-core/src/test/java/de/exlll/configlib/configurations/ExampleInitializer.java @@ -0,0 +1,938 @@ +package de.exlll.configlib.configurations; + +import java.awt.Point; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.Month; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static de.exlll.configlib.TestUtils.*; +import static de.exlll.configlib.configurations.ExampleEnum.*; + +public final class ExampleInitializer { + private static final BigInteger BI_1 = new BigInteger("1"); + private static final BigInteger BI_2 = new BigInteger("2"); + private static final BigInteger BI_3 = new BigInteger("3"); + private static final BigInteger BI_4 = new BigInteger("4"); + private static final BigInteger BI_5 = new BigInteger("5"); + + private static final BigDecimal BD_1 = new BigDecimal("1"); + private static final BigDecimal BD_2 = new BigDecimal("2"); + private static final BigDecimal BD_3 = new BigDecimal("3"); + private static final BigDecimal BD_4 = new BigDecimal("4"); + private static final BigDecimal BD_5 = new BigDecimal("5"); + + private static final LocalDate LD_1 = LocalDate.of(2000, Month.JANUARY, 1); + private static final LocalDate LD_2 = LocalDate.of(2000, Month.JANUARY, 2); + private static final LocalDate LD_3 = LocalDate.of(2000, Month.JANUARY, 3); + private static final LocalDate LD_4 = LocalDate.of(2000, Month.JANUARY, 4); + private static final LocalDate LD_5 = LocalDate.of(2000, Month.JANUARY, 5); + + private static final LocalTime LT_1 = LocalTime.of(0, 1); + private static final LocalTime LT_2 = LocalTime.of(0, 2); + private static final LocalTime LT_3 = LocalTime.of(0, 3); + private static final LocalTime LT_4 = LocalTime.of(0, 4); + private static final LocalTime LT_5 = LocalTime.of(0, 5); + + private static final LocalDateTime LDT_1 = LocalDateTime.of(2000, Month.JANUARY, 1, 0, 0); + private static final LocalDateTime LDT_2 = LocalDateTime.of(2000, Month.JANUARY, 2, 0, 0); + private static final LocalDateTime LDT_3 = LocalDateTime.of(2000, Month.JANUARY, 3, 0, 0); + private static final LocalDateTime LDT_4 = LocalDateTime.of(2000, Month.JANUARY, 4, 0, 0); + private static final LocalDateTime LDT_5 = LocalDateTime.of(2000, Month.JANUARY, 5, 0, 0); + + private static final ExampleConfigurationB1 B1_1 = newExampleConfigurationB1_1(); + private static final ExampleConfigurationB1 B1_2 = newExampleConfigurationB1_2(); + private static final ExampleConfigurationB2 B2_1 = newExampleConfigurationB2_1(); + private static final ExampleConfigurationB2 B2_2 = newExampleConfigurationB2_2(); + + private static final Point P1 = new Point(0, 1); + private static final Point P2 = new Point(0, 2); + private static final Point P3 = new Point(0, 3); + private static final Point P4 = new Point(0, 4); + + public static ExampleConfigurationA2 newExampleConfigurationA2() { + ExampleConfigurationA2 a2 = new ExampleConfigurationA2(); + + a2.setA1_primBool(true); + a2.setA2_primBool(true); + + a2.setA1_primChar('a'); + a2.setA2_primChar('b'); + + a2.setA1_primByte((byte) 1); + a2.setA2_primByte((byte) 2); + + a2.setA1_primShort((short) 3); + a2.setA2_primShort((short) 4); + + a2.setA1_primInt(5); + a2.setA2_primInt(6); + + a2.setA1_primLong(7); + a2.setA2_primLong(8); + + a2.setA1_primFloat(9f); + a2.setA2_primFloat(10f); + + a2.setA1_primDouble(11d); + a2.setA2_primDouble(12d); + + a2.setA1_refBool(true); + a2.setA2_refBool(true); + + a2.setA1_refChar('c'); + a2.setA2_refChar('d'); + + a2.setA1_refByte((byte) 13); + a2.setA2_refByte((byte) 14); + + a2.setA1_refShort((short) 15); + a2.setA2_refShort((short) 16); + + a2.setA1_refInt(17); + a2.setA2_refInt(18); + + a2.setA1_refLong(19L); + a2.setA2_refLong(20L); + + a2.setA1_refFloat(21f); + a2.setA2_refFloat(22f); + + a2.setA1_refDouble(23d); + a2.setA2_refDouble(24d); + + a2.setA1_string("a"); + a2.setA2_string("b"); + + a2.setA1_bigInteger(BI_1); + a2.setA2_bigInteger(BI_2); + + a2.setA1_bigDecimal(BD_1); + a2.setA2_bigDecimal(BD_2); + + a2.setA1_localDate(LD_1); + a2.setA2_localDate(LD_2); + + a2.setA1_localTime(LT_1); + a2.setA2_localTime(LT_2); + + a2.setA1_localDateTime(LDT_1); + a2.setA2_localDateTime(LDT_2); + + a2.setA1_Enm(A); + a2.setA2_Enm(B); + + a2.setA1_b1(B1_1); + a2.setA2_b1(B1_2); + + a2.setA1_b2(B2_1); + a2.setA2_b2(B2_2); + + a2.setA1_listBoolean(List.of(true, false, true)); + a2.setA2_listBoolean(List.of(false, true, false)); + + a2.setA1_listChar(List.of('a', 'b', 'c')); + a2.setA2_listChar(List.of('d', 'e', 'f')); + + a2.setA1_listByte(List.of((byte) 1, (byte) 2, (byte) 3)); + a2.setA2_listByte(List.of((byte) 2, (byte) 3, (byte) 4)); + + a2.setA1_listShort(List.of((short) 1, (short) 2, (short) 3)); + a2.setA2_listShort(List.of((short) 2, (short) 3, (short) 4)); + + a2.setA1_listInteger(List.of(1, 2, 3)); + a2.setA2_listInteger(List.of(2, 3, 4)); + + a2.setA1_listLong(List.of(1L, 2L, 3L)); + a2.setA2_listLong(List.of(2L, 3L, 4L)); + + a2.setA1_listFloat(List.of(1f, 2f, 3f)); + a2.setA2_listFloat(List.of(2f, 3f, 4f)); + + a2.setA1_listDouble(List.of(1d, 2d, 3d)); + a2.setA2_listDouble(List.of(2d, 3d, 4d)); + + a2.setA1_listString(List.of("1", "2", "3")); + a2.setA2_listString(List.of("2", "3", "4")); + + a2.setA1_listBigInteger(List.of(BI_1, BI_2, BI_3)); + a2.setA2_listBigInteger(List.of(BI_2, BI_3, BI_4)); + + a2.setA1_listBigDecimal(List.of(BD_1, BD_2, BD_3)); + a2.setA2_listBigDecimal(List.of(BD_2, BD_3, BD_4)); + + a2.setA1_listLocalDate(List.of(LD_1, LD_2, LD_3)); + a2.setA2_listLocalDate(List.of(LD_2, LD_3, LD_4)); + + a2.setA1_listLocalTime(List.of(LT_1, LT_2, LT_3)); + a2.setA2_listLocalTime(List.of(LT_2, LT_3, LT_4)); + + a2.setA1_listLocalDateTime(List.of(LDT_1, LDT_2, LDT_3)); + a2.setA2_listLocalDateTime(List.of(LDT_2, LDT_3, LDT_4)); + + a2.setA1_listEnm(List.of(A, B, C)); + a2.setA2_listEnm(List.of(B, C, D)); + + a2.setA1_listB1(List.of(B1_1)); + a2.setA2_listB1(List.of(B1_1, B1_2)); + + a2.setA1_listB2(List.of(B2_1)); + a2.setA2_listB2(List.of(B2_1, B2_2)); + + a2.setA1_arrayPrimBoolean(new boolean[]{true, false, true}); + a2.setA2_arrayPrimBoolean(new boolean[]{false, true, false}); + + a2.setA1_arrayPrimChar(new char[]{'a', 'b', 'c'}); + a2.setA2_arrayPrimChar(new char[]{'d', 'e', 'f'}); + + a2.setA1_arrayPrimByte(new byte[]{(byte) 1, (byte) 2, (byte) 3}); + a2.setA2_arrayPrimByte(new byte[]{(byte) 2, (byte) 3, (byte) 4}); + + a2.setA1_arrayPrimShort(new short[]{(short) 1, (short) 2, (short) 3}); + a2.setA2_arrayPrimShort(new short[]{(short) 2, (short) 3, (short) 4}); + + a2.setA1_arrayPrimInteger(new int[]{1, 2, 3}); + a2.setA2_arrayPrimInteger(new int[]{2, 3, 4}); + + a2.setA1_arrayPrimLong(new long[]{1L, 2L, 3L}); + a2.setA2_arrayPrimLong(new long[]{2L, 3L, 4L}); + + a2.setA1_arrayPrimFloat(new float[]{1f, 2f, 3f}); + a2.setA2_arrayPrimFloat(new float[]{2f, 3f, 4f}); + + a2.setA1_arrayPrimDouble(new double[]{1d, 2d, 3d}); + a2.setA2_arrayPrimDouble(new double[]{2d, 3d, 4d}); + + a2.setA1_arrayBoolean(new Boolean[]{true, false, true}); + a2.setA2_arrayBoolean(new Boolean[]{false, true, false}); + + a2.setA1_arrayChar(new Character[]{'a', 'b', 'c'}); + a2.setA2_arrayChar(new Character[]{'d', 'e', 'f'}); + + a2.setA1_arrayByte(new Byte[]{(byte) 1, (byte) 2, (byte) 3}); + a2.setA2_arrayByte(new Byte[]{(byte) 2, (byte) 3, (byte) 4}); + + a2.setA1_arrayShort(new Short[]{(short) 1, (short) 2, (short) 3}); + a2.setA2_arrayShort(new Short[]{(short) 2, (short) 3, (short) 4}); + + a2.setA1_arrayInteger(new Integer[]{1, 2, 3}); + a2.setA2_arrayInteger(new Integer[]{2, 3, 4}); + + a2.setA1_arrayLong(new Long[]{1L, 2L, 3L}); + a2.setA2_arrayLong(new Long[]{2L, 3L, 4L}); + + a2.setA1_arrayFloat(new Float[]{1f, 2f, 3f}); + a2.setA2_arrayFloat(new Float[]{2f, 3f, 4f}); + + a2.setA1_arrayDouble(new Double[]{1d, 2d, 3d}); + a2.setA2_arrayDouble(new Double[]{2d, 3d, 4d}); + + a2.setA1_arrayString(new String[]{"1", "2", "3"}); + a2.setA2_arrayString(new String[]{"2", "3", "4"}); + + a2.setA1_arrayBigInteger(new BigInteger[]{BI_1, BI_2, BI_3}); + a2.setA2_arrayBigInteger(new BigInteger[]{BI_2, BI_3, BI_4}); + + a2.setA1_arrayBigDecimal(new BigDecimal[]{BD_1, BD_2, BD_3}); + a2.setA2_arrayBigDecimal(new BigDecimal[]{BD_2, BD_3, BD_4}); + + a2.setA1_arrayLocalDate(new LocalDate[]{LD_1, LD_2, LD_3}); + a2.setA2_arrayLocalDate(new LocalDate[]{LD_2, LD_3, LD_4}); + + a2.setA1_arrayLocalTime(new LocalTime[]{LT_1, LT_2, LT_3}); + a2.setA2_arrayLocalTime(new LocalTime[]{LT_2, LT_3, LT_4}); + + a2.setA1_arrayLocalDateTime(new LocalDateTime[]{LDT_1, LDT_2, LDT_3}); + a2.setA2_arrayLocalDateTime(new LocalDateTime[]{LDT_2, LDT_3, LDT_4}); + + a2.setA1_arrayEnm(new ExampleEnum[]{A, B, C}); + a2.setA2_arrayEnm(new ExampleEnum[]{B, C, D}); + + a2.setA1_arrayB1(new ExampleConfigurationB1[]{B1_1}); + a2.setA2_arrayB1(new ExampleConfigurationB1[]{B1_1, B1_2}); + + a2.setA1_arrayB2(new ExampleConfigurationB2[]{B2_1}); + a2.setA2_arrayB2(new ExampleConfigurationB2[]{B2_1, B2_2}); + + a2.setA1_setBoolean(asSet(true)); + a2.setA2_setBoolean(asSet(false)); + + a2.setA1_setChar(asSet('a', 'b', 'c')); + a2.setA2_setChar(asSet('d', 'e', 'f')); + + a2.setA1_setByte(asSet((byte) 1, (byte) 2, (byte) 3)); + a2.setA2_setByte(asSet((byte) 2, (byte) 3, (byte) 4)); + + a2.setA1_setShort(asSet((short) 1, (short) 2, (short) 3)); + a2.setA2_setShort(asSet((short) 2, (short) 3, (short) 4)); + + a2.setA1_setInteger(asSet(1, 2, 3)); + a2.setA2_setInteger(asSet(2, 3, 4)); + + a2.setA1_setLong(asSet(1L, 2L, 3L)); + a2.setA2_setLong(asSet(2L, 3L, 4L)); + + a2.setA1_setFloat(asSet(1f, 2f, 3f)); + a2.setA2_setFloat(asSet(2f, 3f, 4f)); + + a2.setA1_setDouble(asSet(1d, 2d, 3d)); + a2.setA2_setDouble(asSet(2d, 3d, 4d)); + + a2.setA1_setString(asSet("1", "2", "3")); + a2.setA2_setString(asSet("2", "3", "4")); + + a2.setA1_setBigInteger(asSet(BI_1, BI_2, BI_3)); + a2.setA2_setBigInteger(asSet(BI_2, BI_3, BI_4)); + + a2.setA1_setBigDecimal(asSet(BD_1, BD_2, BD_3)); + a2.setA2_setBigDecimal(asSet(BD_2, BD_3, BD_4)); + + a2.setA1_setLocalDate(asSet(LD_1, LD_2, LD_3)); + a2.setA2_setLocalDate(asSet(LD_2, LD_3, LD_4)); + + a2.setA1_setLocalTime(asSet(LT_1, LT_2, LT_3)); + a2.setA2_setLocalTime(asSet(LT_2, LT_3, LT_4)); + + a2.setA1_setLocalDateTime(asSet(LDT_1, LDT_2, LDT_3)); + a2.setA2_setLocalDateTime(asSet(LDT_2, LDT_3, LDT_4)); + + a2.setA1_setEnm(asSet(A, B, C)); + a2.setA2_setEnm(asSet(B, C, D)); + + a2.setA1_setB1(asSet(B1_1)); + a2.setA2_setB1(asSet(B1_1, B1_2)); + + a2.setA1_setB2(asSet(B2_1)); + a2.setA2_setB2(asSet(B2_1, B2_2)); + + a2.setA1_mapBooleanBoolean(asMap(true, true, false, false)); + a2.setA2_mapBooleanBoolean(asMap(true, true, false, false)); + + a2.setA1_mapCharChar(asMap('a', 'b', 'c', 'd')); + a2.setA2_mapCharChar(asMap('b', 'c', 'd', 'e')); + + a2.setA1_mapByteByte(asMap((byte) 1, (byte) 2, (byte) 3, (byte) 4)); + a2.setA2_mapByteByte(asMap((byte) 2, (byte) 3, (byte) 4, (byte) 5)); + + a2.setA1_mapShortShort(asMap((short) 1, (short) 2, (short) 3, (short) 4)); + a2.setA2_mapShortShort(asMap((short) 2, (short) 3, (short) 4, (short) 5)); + + a2.setA1_mapIntegerInteger(asMap(1, 2, 3, 4)); + a2.setA2_mapIntegerInteger(asMap(2, 3, 4, 5)); + + a2.setA1_mapLongLong(asMap(1L, 2L, 3L, 4L)); + a2.setA2_mapLongLong(asMap(2L, 3L, 4L, 5L)); + + a2.setA1_mapFloatFloat(asMap(1f, 2f, 3f, 4f)); + a2.setA2_mapFloatFloat(asMap(2f, 3f, 4f, 5f)); + + a2.setA1_mapDoubleDouble(asMap(1d, 2d, 3d, 4d)); + a2.setA2_mapDoubleDouble(asMap(2d, 3d, 4d, 5d)); + + a2.setA1_mapStringString(asMap("1", "2", "3", "4")); + a2.setA2_mapStringString(asMap("2", "3", "4", "5")); + + a2.setA1_mapBigIntegerBigInteger(asMap(BI_1, BI_2, BI_3, BI_4)); + a2.setA2_mapBigIntegerBigInteger(asMap(BI_2, BI_3, BI_4, BI_5)); + + a2.setA1_mapBigDecimalBigDecimal(asMap(BD_1, BD_2, BD_3, BD_4)); + a2.setA2_mapBigDecimalBigDecimal(asMap(BD_2, BD_3, BD_4, BD_5)); + + a2.setA1_mapLocalDateLocalDate(asMap(LD_1, LD_2, LD_3, LD_4)); + a2.setA2_mapLocalDateLocalDate(asMap(LD_2, LD_3, LD_4, LD_5)); + + a2.setA1_mapLocalTimeLocalTime(asMap(LT_1, LT_2, LT_3, LT_4)); + a2.setA2_mapLocalTimeLocalTime(asMap(LT_2, LT_3, LT_4, LT_5)); + + a2.setA1_mapLocalDateTimeLocalDateTime(asMap(LDT_1, LDT_2, LDT_3, LDT_4)); + a2.setA2_mapLocalDateTimeLocalDateTime(asMap(LDT_2, LDT_3, LDT_4, LDT_5)); + + a2.setA1_mapEnmEnm(asMap(A, B, C, D)); + a2.setA2_mapEnmEnm(asMap(B, C, D, E)); + + a2.setA1_mapIntegerB1(asMap(1, B1_1, 2, B1_2)); + a2.setA2_mapIntegerB1(asMap(2, B1_1, 3, B1_2)); + + a2.setA1_mapEnmB2(asMap(A, B2_1, B, B2_2)); + a2.setA2_mapEnmB2(asMap(B, B2_1, C, B2_2)); + + a2.setA1_listEmpty(Collections.emptyList()); + a2.setA2_listEmpty(List.of()); + + a2.setA1_arrayEmpty(new Integer[0]); + a2.setA2_arrayEmpty(new Integer[0]); + + a2.setA1_setEmpty(Collections.emptySet()); + a2.setA2_setEmpty(asSet()); + + a2.setA1_mapEmpty(Collections.emptyMap()); + a2.setA2_mapEmpty(asMap()); + + a2.setA1_listListByte(List.of( + List.of(), + List.of((byte) 1), + List.of((byte) 1, (byte) 2) + )); + a2.setA2_listListByte(List.of( + List.of((byte) 1), + List.of((byte) 1, (byte) 2), + List.of((byte) 1, (byte) 2, (byte) 3) + )); + + a2.setA1_listArrayFloat(List.of( + new Float[0], + new Float[]{1f}, + new Float[]{1f, 2f} + )); + a2.setA2_listArrayFloat(List.of( + new Float[]{1f}, + new Float[]{1f, 2f}, + new Float[]{1f, 2f, 3f} + )); + + a2.setA1_listSetString(List.of( + asSet(), + asSet("1"), + asSet("1", "2") + )); + a2.setA2_listSetString(List.of( + asSet("1"), + asSet("1", "2"), + asSet("1", "2", "3") + )); + + a2.setA1_listMapEnmLocalDate(List.of( + asMap(), + asMap(A, LD_1), + asMap(A, LD_1, B, LD_2) + )); + a2.setA2_listMapEnmLocalDate(List.of( + asMap(A, LD_1), + asMap(A, LD_1, B, LD_2), + asMap(A, LD_1, B, LD_2, C, LD_3) + )); + + a2.setA1_setSetShort(asSet( + asSet(), + asSet((short) 1), + asSet((short) 1, (short) 2) + )); + a2.setA2_setSetShort(asSet( + asSet((short) 1), + asSet((short) 1, (short) 2), + asSet((short) 1, (short) 2, (short) 3) + )); + + a2.setA1_setArrayDouble(asSet( + new Double[0], + new Double[]{1d}, + new Double[]{1d, 2d} + )); + a2.setA2_setArrayDouble(asSet( + new Double[]{1d}, + new Double[]{1d, 2d}, + new Double[]{1d, 2d, 3d} + )); + + a2.setA1_setListString(asSet( + List.of(), + List.of("1"), + List.of("1", "2") + )); + a2.setA2_setListString(asSet( + List.of("1"), + List.of("1", "2"), + List.of("1", "2", "3") + )); + + a2.setA1_setMapEnmLocalTime(asSet( + asMap(), + asMap(A, LT_1), + asMap(A, LT_1, B, LT_2) + )); + a2.setA2_setMapEnmLocalTime(asSet( + asMap(A, LT_1), + asMap(A, LT_1, B, LT_2), + asMap(A, LT_1, B, LT_2, C, LT_3) + )); + + a2.setA1_mapIntegerMapLongBoolean(asMap( + 1, asMap(), + 2, asMap(1L, true), + 3, asMap(1L, true, 2L, false) + )); + a2.setA2_mapIntegerMapLongBoolean(asMap( + 2, asMap(1L, true), + 3, asMap(1L, true, 2L, false), + 4, asMap(1L, true, 2L, false, 3L, true) + )); + + a2.setA1_mapStringListB1(asMap( + "1", List.of(), + "2", List.of(B1_1), + "3", List.of(B1_1, B1_2) + + )); + a2.setA2_mapStringListB1(asMap( + "2", List.of(B1_1), + "3", List.of(B1_1, B1_2), + "4", List.of(B1_1, B1_2, B1_1) + + )); + + a2.setA1_mapBigIntegerArrayBigDecimal(asMap( + BI_1, new BigDecimal[0], + BI_2, new BigDecimal[]{BD_1}, + BI_3, new BigDecimal[]{BD_1, BD_2} + )); + a2.setA2_mapBigIntegerArrayBigDecimal(asMap( + BI_2, new BigDecimal[]{BD_1}, + BI_3, new BigDecimal[]{BD_1, BD_2}, + BI_4, new BigDecimal[]{BD_1, BD_2, BD_3} + )); + + a2.setA1_mapEnmSetB2(asMap( + A, asSet(), + B, asSet(B2_1), + C, asSet(B2_1, B2_2) + )); + a2.setA2_mapEnmSetB2(asMap( + B, asSet(B2_1), + C, asSet(B2_1, B2_2), + D, asSet() + )); + + a2.setA1_mapIntegerListMapShortSetB2(asMap( + 1, List.of(), + 2, List.of(asMap()), + 3, List.of(asMap((short) 1, asSet(), (short) 2, asSet(B2_1))), + 4, List.of(asMap((short) 1, asSet(), (short) 2, asSet(B2_1)), asMap()) + )); + a2.setA2_mapIntegerListMapShortSetB2(asMap( + 2, List.of(asMap()), + 3, List.of(asMap((short) 1, asSet(), (short) 2, asSet(B2_1))), + 4, List.of(asMap((short) 1, asSet(), (short) 2, asSet(B2_1)), asMap()), + 5, List.of( + asMap((short) 1, asSet(), (short) 2, asSet(B2_1)), + asMap((short) 1, asSet()) + ) + )); + + a2.setA1_arrayArrayPrimBoolean(new boolean[][]{ + {}, + {true}, + {true, false}, + }); + a2.setA2_arrayArrayPrimBoolean(new boolean[][]{ + {true}, + {true, false}, + {true, false, true}, + }); + + a2.setA1_arrayArrayPrimChar(new char[][]{ + {}, + {'a'}, + {'a', 'b'} + }); + a2.setA2_arrayArrayPrimChar(new char[][]{ + {'a'}, + {'a', 'b'}, + {'a', 'b', 'c'}, + }); + + a2.setA1_arrayArrayPrimByte(new byte[][]{ + {}, + {(byte) 1}, + {(byte) 1, (byte) 2} + }); + a2.setA2_arrayArrayPrimByte(new byte[][]{ + {1}, + {(byte) 1, (byte) 2}, + {(byte) 1, (byte) 2, (byte) 3}, + }); + + a2.setA1_arrayArrayPrimShort(new short[][]{ + {}, + {(short) 1}, + {(short) 1, (short) 2} + }); + a2.setA2_arrayArrayPrimShort(new short[][]{ + {(short) 1}, + {(short) 1, (short) 2}, + {(short) 1, (short) 2, (short) 3}, + }); + + a2.setA1_arrayArrayPrimInteger(new int[][]{ + {}, + {1}, + {1, 2} + }); + a2.setA2_arrayArrayPrimInteger(new int[][]{ + {1}, + {1, 2}, + {1, 2, 3}, + }); + + a2.setA1_arrayArrayPrimLong(new long[][]{ + {}, + {1L}, + {1L, 2L} + }); + a2.setA2_arrayArrayPrimLong(new long[][]{ + {1L}, + {1L, 2L}, + {1L, 2L, 3L}, + }); + + a2.setA1_arrayArrayPrimFloat(new float[][]{ + {}, + {1f}, + {1f, 2f} + }); + a2.setA2_arrayArrayPrimFloat(new float[][]{ + {1f}, + {1f, 2f}, + {1f, 2f, 3f}, + }); + + a2.setA1_arrayArrayPrimDouble(new double[][]{ + {}, + {1d}, + {1d, 2d} + }); + a2.setA2_arrayArrayPrimDouble(new double[][]{ + {1d}, + {1d, 2d}, + {1d, 2d, 3d}, + }); + + a2.setA1_arrayArrayBoolean(new Boolean[][]{ + {}, + {true}, + {true, false}, + }); + a2.setA2_arrayArrayBoolean(new Boolean[][]{ + {true}, + {true, false}, + {true, false, true}, + }); + + a2.setA1_arrayArrayChar(new Character[][]{ + {}, + {'a'}, + {'a', 'b'} + }); + a2.setA2_arrayArrayChar(new Character[][]{ + {'a'}, + {'a', 'b'}, + {'a', 'b', 'c'}, + }); + + a2.setA1_arrayArrayByte(new Byte[][]{ + {}, + {(byte) 1}, + {(byte) 1, (byte) 2} + }); + a2.setA2_arrayArrayByte(new Byte[][]{ + {1}, + {(byte) 1, (byte) 2}, + {(byte) 1, (byte) 2, (byte) 3}, + }); + + a2.setA1_arrayArrayShort(new Short[][]{ + {}, + {(short) 1}, + {(short) 1, (short) 2} + }); + a2.setA2_arrayArrayShort(new Short[][]{ + {(short) 1}, + {(short) 1, (short) 2}, + {(short) 1, (short) 2, (short) 3}, + }); + + a2.setA1_arrayArrayInteger(new Integer[][]{ + {}, + {1}, + {1, 2} + }); + a2.setA2_arrayArrayInteger(new Integer[][]{ + {1}, + {1, 2}, + {1, 2, 3}, + }); + + a2.setA1_arrayArrayLong(new Long[][]{ + {}, + {1L}, + {1L, 2L} + }); + a2.setA2_arrayArrayLong(new Long[][]{ + {1L}, + {1L, 2L}, + {1L, 2L, 3L}, + }); + + a2.setA1_arrayArrayFloat(new Float[][]{ + {}, + {1f}, + {1f, 2f} + }); + a2.setA2_arrayArrayFloat(new Float[][]{ + {1f}, + {1f, 2f}, + {1f, 2f, 3f}, + }); + + a2.setA1_arrayArrayDouble(new Double[][]{ + {}, + {1d}, + {1d, 2d} + }); + a2.setA2_arrayArrayDouble(new Double[][]{ + {1d}, + {1d, 2d}, + {1d, 2d, 3d}, + }); + + a2.setA1_arrayArrayString(new String[][]{ + {}, + {"a"}, + {"a", "b"} + }); + a2.setA2_arrayArrayString(new String[][]{ + {"a"}, + {"a", "b"}, + {"a", "b", "c"}, + }); + + a2.setA1_arrayArrayBigInteger(new BigInteger[][]{ + {}, + {BI_1}, + {BI_1, BI_2}, + }); + a2.setA2_arrayArrayBigInteger(new BigInteger[][]{ + {BI_1}, + {BI_1, BI_2}, + {BI_1, BI_2, BI_3}, + }); + + a2.setA1_arrayArrayBigDecimal(new BigDecimal[][]{ + {}, + {BD_1}, + {BD_1, BD_2}, + }); + a2.setA2_arrayArrayBigDecimal(new BigDecimal[][]{ + {BD_1}, + {BD_1, BD_2}, + {BD_1, BD_2, BD_3}, + }); + + a2.setA1_arrayArrayLocalDate(new LocalDate[][]{ + {}, + {LD_1}, + {LD_1, LD_2}, + }); + a2.setA2_arrayArrayLocalDate(new LocalDate[][]{ + {LD_1}, + {LD_1, LD_2}, + {LD_1, LD_2, LD_3}, + }); + + a2.setA1_arrayArrayLocalTime(new LocalTime[][]{ + {}, + {LT_1}, + {LT_1, LT_2}, + }); + a2.setA2_arrayArrayLocalTime(new LocalTime[][]{ + {LT_1}, + {LT_1, LT_2}, + {LT_1, LT_2, LT_3}, + }); + + a2.setA1_arrayArrayLocalDateTime(new LocalDateTime[][]{ + {}, + {LDT_1}, + {LDT_1, LDT_2}, + }); + a2.setA2_arrayArrayLocalDateTime(new LocalDateTime[][]{ + {LDT_1}, + {LDT_1, LDT_2}, + {LDT_1, LDT_2, LDT_3}, + }); + + a2.setA1_arrayArrayEnm(new ExampleEnum[][]{ + {}, + {A}, + {A, B}, + }); + a2.setA2_arrayArrayEnm(new ExampleEnum[][]{ + {A}, + {A, B}, + {A, B, C}, + }); + + a2.setA1_arrayArrayB1(new ExampleConfigurationB1[][]{{}, {B1_1}}); + a2.setA2_arrayArrayB1(new ExampleConfigurationB1[][]{{B1_1}, {B1_2}}); + + a2.setA1_arrayArrayB2(new ExampleConfigurationB2[][]{{}, {B2_1}}); + a2.setA2_arrayArrayB2(new ExampleConfigurationB2[][]{{B2_1}, {B2_2}}); + + a2.setA1_point(P1); + a2.setA2_point(P2); + + a2.setA1_listPoint(List.of(P1, P2, P3)); + a2.setA2_listPoint(List.of(P2, P3, P4)); + + a2.setA1_arrayPoint(new Point[]{P1, P2, P3}); + a2.setA2_arrayPoint(new Point[]{P2, P3, P4}); + + a2.setA1_setPoint(asSet(P1, P2, P3)); + a2.setA2_setPoint(asSet(P2, P3, P4)); + + a2.setA1_mapEnmListPoint(asMap( + A, List.of(), + B, List.of(P1), + C, List.of(P1, P2) + )); + a2.setA2_mapEnmListPoint(asMap( + B, List.of(P1), + C, List.of(P1, P2), + D, List.of(P1, P2, P3) + )); + + return a2; + } + + public static ExampleConfigurationB1 newExampleConfigurationB1_1() { + ExampleConfigurationB1 b1 = new ExampleConfigurationB1(); + initializeExampleConfigurationB1_1(b1); + return b1; + } + + private static void initializeExampleConfigurationB1_1(ExampleConfigurationB1 b1) { + b1.setB1_primBool(true); + b1.setB1_refChar('b'); + b1.setB1_string("c"); + b1.setB1_listByte(List.of((byte) 1, (byte) 2, (byte) 3)); + b1.setB1_arrayShort(new Short[]{1, 2, 3}); + b1.setB1_setInteger(asSet(1, 2, 3)); + b1.setB1_listEmpty(Collections.emptyList()); + b1.setB1_mapLongLong(asMap(1L, 2L, 3L, 4L)); + b1.setB1_listListByte(List.of( + List.of((byte) 1, (byte) 2, (byte) 3), + List.of((byte) 2, (byte) 3, (byte) 4) + )); + b1.setB1_point(new Point(1, 2)); + } + + public static ExampleConfigurationB1 newExampleConfigurationB1_2() { + ExampleConfigurationB1 b1 = new ExampleConfigurationB1(); + initializeExampleConfigurationB1_2(b1); + return b1; + } + + private static void initializeExampleConfigurationB1_2(ExampleConfigurationB1 b1) { + b1.setB1_primBool(false); + b1.setB1_refChar('c'); + b1.setB1_string("d"); + b1.setB1_listByte(List.of((byte) 2, (byte) 3, (byte) 4)); + b1.setB1_arrayShort(new Short[]{2, 3, 4}); + b1.setB1_setInteger(asSet(2, 3, 4)); + b1.setB1_listEmpty(Collections.emptyList()); + b1.setB1_mapLongLong(asMap(2L, 3L, 4L, 5L)); + b1.setB1_listListByte(List.of( + List.of((byte) 2, (byte) 3, (byte) 4), + List.of((byte) 3, (byte) 4, (byte) 5) + )); + b1.setB1_point(new Point(1, 3)); + } + + public static ExampleConfigurationB2 newExampleConfigurationB2_1() { + ExampleConfigurationB2 b2 = new ExampleConfigurationB2(); + initializeExampleConfigurationB1_1(b2); + b2.setB2_primChar('a'); + b2.setB2_refBool(true); + b2.setB2_bigInteger(BI_1); + b2.setB2_listShort(List.of((short) 2, (short) 3, (short) 4)); + b2.setB2_arrayInteger(new Integer[]{2, 3, 4}); + b2.setB2_setLong(asSet(2L, 3L, 4L)); + b2.setB2_arrayEmpty(new BigInteger[0]); + b2.setB2_mapFloatFloat(asMap(1f, 2f, 3f, 4f)); + b2.setB2_setArrayDouble(asSet( + new Double[]{1d, 2d, 3d, 4d}, + new Double[]{5d, 6d, 7d, 8d} + )); + b2.setB2_listPoint(List.of( + new Point(1, 2), + new Point(3, 4) + )); + return b2; + } + + public static ExampleConfigurationB2 newExampleConfigurationB2_2() { + ExampleConfigurationB2 b2 = new ExampleConfigurationB2(); + initializeExampleConfigurationB1_2(b2); + b2.setB2_primChar('b'); + b2.setB2_refBool(false); + b2.setB2_bigInteger(BI_2); + b2.setB2_listShort(List.of((short) 3, (short) 4, (short) 5)); + b2.setB2_arrayInteger(new Integer[]{3, 4, 5}); + b2.setB2_setLong(asSet(3L, 4L, 5L)); + b2.setB2_arrayEmpty(new BigInteger[0]); + b2.setB2_mapFloatFloat(asMap(2f, 3f, 4f, 5f)); + b2.setB2_setArrayDouble(asSet( + new Double[]{2d, 3d, 4d, 5d}, + new Double[]{6d, 7d, 8d, 9d} + )); + b2.setB2_listPoint(List.of( + new Point(2, 3), + new Point(4, 5) + )); + return b2; + } + + public static ExampleConfigurationNulls newExampleConfigurationNullsWithNullCollectionElements1() { + ExampleConfigurationNulls result = new ExampleConfigurationNulls(); + result.setListNullString(Arrays.asList(null, "a", null)); + result.setArrayNullDouble(new Double[]{null, 1d, null}); + result.setSetNullInteger(asSet((Integer) null)); + result.setMapNullEnmKey(entriesAsMap(entry(null, BI_1))); + result.setMapNullBigIntegerValue(entriesAsMap(entry(A, null))); + return result; + } + + public static ExampleConfigurationNulls newExampleConfigurationNullsWithNullCollectionElements2() { + ExampleConfigurationNulls result = new ExampleConfigurationNulls(); + result.setListNullString(Arrays.asList("b", null, "c")); + result.setArrayNullDouble(new Double[]{2d, null, 3d}); + result.setSetNullInteger(asSet(null, 1)); + result.setMapNullEnmKey(entriesAsMap(entry(null, BI_1), entry(A, BI_2))); + result.setMapNullBigIntegerValue(entriesAsMap(entry(A, null), entry(B, BI_1))); + return result; + } + + public static ExampleConfigurationNulls newExampleConfigurationNullsWithoutNullCollectionElements1() { + ExampleConfigurationNulls result = new ExampleConfigurationNulls(); + result.setListNullString(List.of("a")); + result.setArrayNullDouble(new Double[]{1d}); + result.setSetNullInteger(asSet()); + result.setMapNullEnmKey(entriesAsMap()); + result.setMapNullBigIntegerValue(entriesAsMap()); + return result; + } + + public static ExampleConfigurationNulls newExampleConfigurationNullsWithoutNullCollectionElements2() { + ExampleConfigurationNulls result = new ExampleConfigurationNulls(); + result.setListNullString(Arrays.asList("b", "c")); + result.setArrayNullDouble(new Double[]{2d, 3d}); + result.setSetNullInteger(asSet(1)); + result.setMapNullEnmKey(entriesAsMap(entry(A, BI_2))); + result.setMapNullBigIntegerValue(entriesAsMap(entry(B, BI_1))); + return result; + } +} diff --git a/configlib-paper/build.gradle.kts b/configlib-paper/build.gradle.kts new file mode 100644 index 0000000..bdb7400 --- /dev/null +++ b/configlib-paper/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + `java-config` +} + +dependencies { + shade(project(":configlib-core")) + implementation("io.papermc.paper:paper-api:1.19-R0.1-SNAPSHOT") +} + +tasks.jar { from(project(":configlib-core").sourceSets["main"].output) } \ No newline at end of file diff --git a/configlib-paper/src/main/java/de/exlll/configlib/ConfigLib.java b/configlib-paper/src/main/java/de/exlll/configlib/ConfigLib.java new file mode 100644 index 0000000..3ebabc4 --- /dev/null +++ b/configlib-paper/src/main/java/de/exlll/configlib/ConfigLib.java @@ -0,0 +1,8 @@ +package de.exlll.configlib; + +import org.bukkit.plugin.java.JavaPlugin; + +/** + * An empty plugin class that loads this library and its dependencies. + */ +public final class ConfigLib extends JavaPlugin {} diff --git a/configlib-paper/src/main/resources/plugin.yml b/configlib-paper/src/main/resources/plugin.yml new file mode 100644 index 0000000..7cb8274 --- /dev/null +++ b/configlib-paper/src/main/resources/plugin.yml @@ -0,0 +1,7 @@ +name: ConfigLib +version: 3.0.0 +website: https://github.com/Exlll/ConfigLib +description: A library for working with YAML configurations. +author: Exlll +main: de.exlll.configlib.ConfigLib +api-version: 1.19 \ No newline at end of file diff --git a/configlib-velocity/build.gradle.kts b/configlib-velocity/build.gradle.kts new file mode 100644 index 0000000..9a6c87e --- /dev/null +++ b/configlib-velocity/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + `java-config` +} + +dependencies { + shade(project(":configlib-core")) + implementation("com.velocitypowered:velocity-api:3.0.1") + annotationProcessor("com.velocitypowered:velocity-api:3.0.1") +} + +tasks.jar { from(project(":configlib-core").sourceSets["main"].output) } \ No newline at end of file diff --git a/configlib-velocity/src/main/java/de/exlll/configlib/ConfigLib.java b/configlib-velocity/src/main/java/de/exlll/configlib/ConfigLib.java new file mode 100644 index 0000000..7f2a976 --- /dev/null +++ b/configlib-velocity/src/main/java/de/exlll/configlib/ConfigLib.java @@ -0,0 +1,16 @@ +package de.exlll.configlib; + +import com.velocitypowered.api.plugin.Plugin; + +/** + * An empty plugin class that loads this library and its dependencies. + */ +@Plugin( + id = "configlib", + name = "ConfigLib", + version = "3.0.0", + url = "https://github.com/Exlll/ConfigLib", + description = "A library for working with YAML configurations.", + authors = {"Exlll"} +) +public final class ConfigLib {} diff --git a/configlib-waterfall/build.gradle.kts b/configlib-waterfall/build.gradle.kts new file mode 100644 index 0000000..4338ac7 --- /dev/null +++ b/configlib-waterfall/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + `java-config` +} + +dependencies { + shade(project(":configlib-core")) + implementation("io.github.waterfallmc:waterfall-api:1.19-R0.1-SNAPSHOT") +} + +tasks.jar { from(project(":configlib-core").sourceSets["main"].output) } \ No newline at end of file diff --git a/configlib-waterfall/src/main/java/de/exlll/configlib/ConfigLib.java b/configlib-waterfall/src/main/java/de/exlll/configlib/ConfigLib.java new file mode 100644 index 0000000..19118d1 --- /dev/null +++ b/configlib-waterfall/src/main/java/de/exlll/configlib/ConfigLib.java @@ -0,0 +1,8 @@ +package de.exlll.configlib; + +import net.md_5.bungee.api.plugin.Plugin; + +/** + * An empty plugin class that loads this library and its dependencies. + */ +public final class ConfigLib extends Plugin {} diff --git a/configlib-waterfall/src/main/resources/plugin.yml b/configlib-waterfall/src/main/resources/plugin.yml new file mode 100644 index 0000000..54782d9 --- /dev/null +++ b/configlib-waterfall/src/main/resources/plugin.yml @@ -0,0 +1,6 @@ +name: ConfigLib +version: 3.0.0 +website: https://github.com/Exlll/ConfigLib +description: A library for working with YAML configurations. +author: Exlll +main: de.exlll.configlib.ConfigLib \ No newline at end of file diff --git a/docs/custom-config.md b/docs/custom-config.md deleted file mode 100644 index ae610a4..0000000 --- a/docs/custom-config.md +++ /dev/null @@ -1,181 +0,0 @@ -This tutorial is intended to show how to add a custom configuration source. So, let's say you -want to implement your own `Configuration` type, an `InMemoryConfiguration`, for example. - -#### 1. Extend `Configuration` - -The first thing you have to do is to let your `InMemoryConfiguration` class -extend `Configuration`. - -```java -import de.exlll.configlib.Configuration; -import de.exlll.configlib.ConfigurationSource; - -public class InMemoryConfiguration extends Configuration {} -``` - -#### 2. Create a `ConfigurationSource` - -The second step is to create a class that implements -`ConfigurationSource`. - -```java -final class InMemoryConfigurationSource - implements ConfigurationSource {} -``` - -#### 3. Implement `save-/loadConfiguration` - -Next implement its methods. - -```java -final class InMemoryConfigurationSource - implements ConfigurationSource { - private Map configAsMap = new HashMap<>(); - - /* The 'config' parameter is the Configuration instance that - * requested the save/load */ - @Override - public void saveConfiguration( - InMemoryConfiguration config, Map map - ) { - this.configAsMap = map; - } - - @Override - public Map loadConfiguration( - InMemoryConfiguration config - ) { - return configAsMap; - } -} -``` -#### 4. Extend `Properties` and `Builder` - -Within your `InMemoryConfiguration` class, create an `InMemoryProperties` class -that extends `Configuration.Properties`. Within the `InMemoryProperties` class, -create a `Builder` class that extends `Properties.Builder`. - -```java -public class InMemoryConfiguration extends Configuration { - - public static final class InMemoryProperties extends Properties { - protected InMemoryProperties(Builder builder) { - super(builder); - } - - public static Builder builder() { - return new Builder() { - @Override - protected Builder getThis() { - return this; - } - }; - } - - public static abstract class Builder> - extends Properties.Builder { - public InMemoryProperties build() { - return new InMemoryProperties(this); - } - } - } -} -``` - -#### 5. Add properties - -Add properties to your `InMemoryProperties` class that can configure your -`InMemoryConfiguration` in some meaningful way. For example: - -```java -public static final class InMemoryProperties extends Properties { - private final int minMemory; - private final int maxMemory; - - protected InMemoryProperties(Builder builder) { - super(builder); - this.minMemory = builder.minMemory; - this.maxMemory = builder.maxMemory; - } - - public int getMinMemory() { return minMemory; } - - public int getMaxMemory() { return maxMemory; } - - public static Builder builder() { - return new Builder() { - @Override protected Builder getThis() { return this; } - }; - } - - public static abstract class Builder> - extends Properties.Builder { - private int minMemory = 0; - private int maxMemory = 1024; - - public B setMinMemory(int minMemory) { - this.minMemory = minMemory; - return getThis(); - } - - public B setMaxMemory(int maxMemory) { - this.maxMemory = maxMemory; - return getThis(); - } - - public InMemoryProperties build() { - return new InMemoryProperties(this); - } - } -} -``` - -#### 6. Implement `getSource/getThis` - -The last step is to implement the `getSource` and `getThis` methods and override the -constructor of your `InMemoryConfiguration`. - -```java -public class InMemoryConfiguration extends Configuration { - private final InMemoryConfigurationSource source = - new InMemoryConfigurationSource(); - - protected InMemoryConfiguration(InMemoryProperties properties) { - super(properties); - } - - @Override - protected ConfigurationSource getSource() { - return source; - } - - @Override - protected InMemoryConfiguration getThis() { - return this; - } -} -``` - -#### 7. Use your new `InMemoryConfiguration` - -```java -final class InMemoryDatabaseConfig extends InMemoryConfiguration { - private final String host = "localhost"; - // ... - - public InMemoryDatabaseConfig(InMemoryProperties properties) { - super(properties); - } - // ... - - public static void main(String[] args) { - InMemoryProperties properties = InMemoryProperties.builder() - .setMinMemory(123) - .setMaxMemory(456) - .build(); - - InMemoryDatabaseConfig config = new InMemoryDatabaseConfig(properties); - config.save(); - } -} -``` \ No newline at end of file diff --git a/docs/tutorial.md b/docs/tutorial.md deleted file mode 100644 index 23a46bc..0000000 --- a/docs/tutorial.md +++ /dev/null @@ -1,537 +0,0 @@ -This tutorial is intended to show most of the features of this library, so let's say that -we want to create the following configuration file for some kind of game: - -```yaml -# Valid color codes: &4, &c, &e -win_message: '&4YOU WON' -blocked_users: -- root -- john -team_members: -- - Pete - - Mary - - Alice - - Leo -- - Eli - - Eve - - Paul - - Patrick -moderator: - credentials: - username: alex - password: '123' - email: a@b.c -users_by_name: - Patrick: - credentials: - username: Patrick - password: '579' - email: patrick@example.com - Eli: - credentials: - username: Eli - password: '246' - email: eli@example.com - Pete: - credentials: - username: Pete - password: '123' - email: pete@example.com - Eve: - credentials: - username: Eve - password: '357' - email: eve@example.com - Alice: - credentials: - username: Alice - password: '789' - email: alice@example.com - Leo: - credentials: - username: Leo - password: '135' - email: leo@example.com - Paul: - credentials: - username: Paul - password: '468' - email: paul@example.com - Mary: - credentials: - username: Mary - password: '456' - email: mary@example.com -first_prize: - ==: org.bukkit.inventory.ItemStack - type: DIAMOND_AXE - meta: - ==: ItemMeta - meta-type: UNSPECIFIC - enchants: - DIG_SPEED: 5 - DURABILITY: 3 - MENDING: 1 -consolation_prizes: -- ==: org.bukkit.inventory.ItemStack - type: STICK - amount: 2 -- ==: org.bukkit.inventory.ItemStack - type: ROTTEN_FLESH - amount: 3 -- ==: org.bukkit.inventory.ItemStack - type: CARROT - amount: 4 -prohibited_items: -- BEDROCK -- AIR -- LAVA - -# Configure the arena: -arena_height: 40 -arena_center: world;0;128;0 - -# Remember to play fair! -``` - -#### 1. Extend `BukkitYamlConfiguration` - -The first thing we have to do is to extend `BukkitYamlConfiguration`. We use -`BukkitYamlConfiguration` instead of `YamlConfiguration` because it can properly -(de-)serialize Bukkit classes like `ItemStack`s. - -```java -public final class GameConfig extends BukkitYamlConfiguration {} -``` - -#### 2. Create constructor - -Next we have to create a constructor that matches super. If we don't pass a -`BukkitYamlProperties` object to the super call, the `BukkitYamlProperties.DEFAULT` -instance is used. But because we want to format the field names of our configuration -(so that we can write 'winMessage' in Java which becomes 'win_message' in YAML), we -have to pass a `BukkitYamlProperties` object that uses a different -`FieldNameFormatter` (see [11.](https://github.com/Exlll/ConfigLib/wiki/Tutorial#11-use-gameconfig)). - -```java -public final class GameConfig extends BukkitYamlConfiguration { - public GameConfig(Path path, BukkitYamlProperties properties) { - super(path, properties); - } - - // uses BukkitYamlProperties.DEFAULT instance - // public GameConfig(Path path) { - // super(path); - // } -} -``` - -#### 3. Add `winMessage` - -Because `winMessage` is a string with a comment, we add a field named `winMessage` -of type `String` to our configuration class and annotate it with the `@Comment` -annotation. - -```java -public final class GameConfig extends BukkitYamlConfiguration { - @Comment("Valid color codes: &4, &c, &e") - private String winMessage = "&4YOU WON"; - // ... -} -``` - -#### 4. Add `blockedUsers` - -Because `blockedUsers` is a list of strings, we add a field named `blockedUsers` -of type `List` to our configuration class. - -```java -public final class GameConfig extends BukkitYamlConfiguration { - // ... - private List blockedUsers = Arrays.asList("root", "john"); - // ... -} -``` - -Remember that `null` values are not allowed. All non-primitive fields must be -assigned some non-`null` default value. - -#### 5. Add `teamMembers` - -The `teamMembers` field is of type `List>`. - -```java -public final class GameConfig extends BukkitYamlConfiguration { - // ... - private List> teamMembers = Arrays.asList( - Arrays.asList("Pete", "Mary", "Alice", "Leo"), - Arrays.asList("Eli", "Eve", "Paul", "Patrick") - ); - // ... -} -``` - -#### 6. Add `moderator` - -Because our `moderator` represents a user that has credentials and an email address, -we create a `User` and a `Credentials` class and annotate them as -`ConfigurationElement`s: - -```java -@ConfigurationElement -final class Credentials { - private String username; - private String password; - - // ConfigurationElements must have a no-args constructor (can be private) - private Credentials() { this("", ""); } - - public Credentials(String username, String password) { - this.username = username; - this.password = password; - } - // getter etc. - } - -@ConfigurationElement -final class User { - private Credentials credentials; - private String email; - - private User() { this("", "", ""); } - - public User(String username, String password, String email) { - this.credentials = new Credentials(username, password); - this.email = email; - } - // getter etc. -} -``` - -Now we can use the `User` class for our `moderator` field: - -```java -public final class GameConfig extends BukkitYamlConfiguration { - // ... - private User moderator = new User("alex", "123", "a@b.c"); - // ... -} -``` - -`ConfigurationElement`s must have a no-args constructor which is used to create -instances of a given element. - -#### 7. Add `usersByName` - -The `usersByName` field is a map that maps user names to `User` instances. -That means we have to use the `@ElementType` annotation. - -```java -public final class GameConfig extends BukkitYamlConfiguration { - // ... - @ElementType(User.class) - private Map usersByName = initUsersByName(); - // ... - - private Map initUsersByName() { - Map usersByName = new HashMap<>(); - usersByName.put("Pete", new User("Pete", "123", "pete@example.com")); - usersByName.put("Mary", new User("Mary", "456", "mary@example.com")); - // ... - return usersByName; - } -} -``` - -#### 8. Add `firstPrize` and `consolationPrizes` - -The types of `firstPrize` and `consolationPrizes` are `ItemStack` and `List`, -respectively. Because a `BukkitYamlConfiguration` knows how to serialize `ItemStack` -instances, we need to tell the library not to try to convert them. This can be done -by using the `@NoConvert` annotation. - -```java -public final class GameConfig extends BukkitYamlConfiguration { - // ... - @NoConvert - private ItemStack firstPrize = initFirstPrize(); - @NoConvert - private List consolationPrizes = Arrays.asList( - new ItemStack(Material.STICK, 2), - new ItemStack(Material.ROTTEN_FLESH, 3), - new ItemStack(Material.CARROT, 4) - ); - // ... - - private ItemStack initFirstPrize() { - ItemStack stack = new ItemStack(Material.DIAMOND_AXE); - stack.addEnchantment(Enchantment.DURABILITY, 3); - stack.addEnchantment(Enchantment.DIG_SPEED, 5); - stack.addEnchantment(Enchantment.MENDING, 1); - return stack; - } - // ... -} -``` - -#### 9. Add `prohibitedItems` - -The `prohibitedItems` field is a list of `Material`s. Since this library supports -converting enums, we just have to use the `@ElementType` annotation. - -```java -public final class GameConfig extends BukkitYamlConfiguration { - // ... - @ElementType(Material.class) - private List prohibitedItems = Arrays.asList( - Material.BEDROCK, Material.AIR, Material.LAVA - ); - // ... -} -``` - -#### 10. Add `arenaHeight` and `arenaCenter` - -The `arenaHeight` can simply be represented by an `int` field. The `arenaCenter` -is of type `Location`. We could again use the `@NoConvert` annotation but this -would result in a different representation. Instead, we are going to implement our -own `Converter`. - -First we have to create a class that implements `Converter`: - -```java -final class LocationStringConverter implements Converter {} -``` - -Then we must implement the `convertTo` and `convertFrom` methods: - -```java -final class LocationStringConverter implements Converter { - - @Override - public String convertTo(Location location, ConversionInfo conversionInfo) { - String worldName = location.getWorld().getName(); - int blockX = location.getBlockX(); - int blockY = location.getBlockY(); - int blockZ = location.getBlockZ(); - return worldName + ";" + blockX + ";" + blockY + ";" + blockZ; - } - - @Override - public Location convertFrom(String s, ConversionInfo conversionInfo) { - String[] split = s.split(";"); - World world = Bukkit.getWorld(split[0]); - int x = Integer.parseInt(split[1]); - int y = Integer.parseInt(split[2]); - int z = Integer.parseInt(split[3]); - return new Location(world, x, y, z); - } -} -``` - -Finally we have to tell our configuration to use this converter for the -`arenaCenter` field. This is done using the `@Convert` annotation: - -```java -public final class GameConfig extends BukkitYamlConfiguration { - // ... - @Comment({"", "Configure the arena:"}) - private int arenaHeight = 40; - @Convert(LocationStringConverter.class) - private Location arenaCenter = new Location( - Bukkit.getWorld("world"), 0, 128, 0 - ); -} -``` - -#### 11. Use `GameConfig` - -Before we can use our new configuration, we have to instantiate it by passing -a `Path` and a `BukkitYamlProperties` object to its constructor. In this case -the `BukkitYamlProperties` is used to change the formatting of field names, -to append text to the configuration file and to add an additional field filter. - -```java -public final class GamePlugin extends JavaPlugin { - - @Override - public void onEnable() { - Path configPath = new File(getDataFolder(), "config.yml").toPath(); - - BukkitYamlProperties properties = BukkitYamlProperties.builder() - .addFilter(field -> !field.getName().startsWith("ignore")) - .setFormatter(FieldNameFormatters.LOWER_UNDERSCORE) - .setAppendedComments(Arrays.asList( - "", "Remember to play fair!" - )) - .build(); - GameConfig config = new GameConfig(configPath, properties); - config.loadAndSave(); - } -} -``` - -### Full example - -```java -import de.exlll.configlib.Converter; -import de.exlll.configlib.annotation.*; -import de.exlll.configlib.configs.yaml.BukkitYamlConfiguration; -import de.exlll.configlib.configs.yaml.BukkitYamlConfiguration.BukkitYamlProperties; -import de.exlll.configlib.format.FieldNameFormatters; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.enchantments.Enchantment; -import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.java.JavaPlugin; - -import java.io.File; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public final class GamePlugin extends JavaPlugin { - @Override - public void onEnable() { - Path configPath = new File(getDataFolder(), "config.yml").toPath(); - - BukkitYamlProperties properties = BukkitYamlProperties.builder() - .addFilter(field -> !field.getName().startsWith("ignore")) - .setFormatter(FieldNameFormatters.LOWER_UNDERSCORE) - .setAppendedComments(Arrays.asList( - "", "Remember to play fair!" - )) - .build(); - GameConfig config = new GameConfig(configPath, properties); - config.loadAndSave(); - } -} - -final class GameConfig extends BukkitYamlConfiguration { - @Comment("Valid color codes: &4, &c, &e") - private String winMessage = "&4YOU WON"; - private User moderator = new User("alex", "123", "a@b.c"); - private List blockedUsers = Arrays.asList("root", "john"); - private List> teamMembers = Arrays.asList( - Arrays.asList("Pete", "Mary", "Alice", "Leo"), - Arrays.asList("Eli", "Eve", "Paul", "Patrick") - ); - @ElementType(User.class) - private Map usersByName = initUsersByName(); - @NoConvert - private ItemStack firstPrize = initFirstPrize(); - @NoConvert - private List consolationPrizes = Arrays.asList( - new ItemStack(Material.STICK, 2), - new ItemStack(Material.ROTTEN_FLESH, 3), - new ItemStack(Material.CARROT, 4) - ); - @ElementType(Material.class) - private List prohibitedItems = Arrays.asList( - Material.BEDROCK, Material.AIR, Material.LAVA - ); - @Comment({"", "Configure the arena:"}) - private int arenaHeight = 40; - @Convert(LocationStringConverter.class) - private Location arenaCenter = new Location( - Bukkit.getWorld("world"), 0, 128, 0 - ); - private String ignoreMe = "1"; - private String ignoreMeToo = "2"; - - public GameConfig(Path path, BukkitYamlProperties properties) { - super(path, properties); - } - - private Map initUsersByName() { - Map usersByName = new HashMap<>(); - usersByName.put("Pete", new User("Pete", "123", "pete@example.com")); - usersByName.put("Mary", new User("Mary", "456", "mary@example.com")); - usersByName.put("Alice", new User("Alice", "789", "alice@example.com")); - usersByName.put("Leo", new User("Leo", "135", "leo@example.com")); - usersByName.put("Eli", new User("Eli", "246", "eli@example.com")); - usersByName.put("Eve", new User("Eve", "357", "eve@example.com")); - usersByName.put("Paul", new User("Paul", "468", "paul@example.com")); - usersByName.put("Patrick", new User("Patrick", "579", "patrick@example.com")); - return usersByName; - } - - private ItemStack initFirstPrize() { - ItemStack stack = new ItemStack(Material.DIAMOND_AXE); - stack.addEnchantment(Enchantment.DURABILITY, 3); - stack.addEnchantment(Enchantment.DIG_SPEED, 5); - stack.addEnchantment(Enchantment.MENDING, 1); - return stack; - } - - private static final class LocationStringConverter - implements Converter { - - @Override - public String convertTo(Location location, ConversionInfo conversionInfo) { - String worldName = location.getWorld().getName(); - int blockX = location.getBlockX(); - int blockY = location.getBlockY(); - int blockZ = location.getBlockZ(); - return worldName + ";" + blockX + ";" + blockY + ";" + blockZ; - } - - @Override - public Location convertFrom(String s, ConversionInfo conversionInfo) { - String[] split = s.split(";"); - World world = Bukkit.getWorld(split[0]); - int x = Integer.parseInt(split[1]); - int y = Integer.parseInt(split[2]); - int z = Integer.parseInt(split[3]); - return new Location(world, x, y, z); - } - } -} - -@ConfigurationElement -final class User { - private Credentials credentials; - private String email; - - private User() { this("", "", ""); } - - public User(String username, String password, String email) { - this.credentials = new Credentials(username, password); - this.email = email; - } - - public Credentials getCredentials() { - return credentials; - } - - public String getEmail() { - return email; - } -} - -@ConfigurationElement -final class Credentials { - private String username; - private String password; - - // ConfigurationElements must have a no-args constructor (can be private) - private Credentials() { this("", ""); } - - public Credentials(String username, String password) { - this.username = username; - this.password = password; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } -} -``` \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 69a9715..aa991fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 7293ea3..a5f4bf0 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,8 +1,5 @@ rootProject.name = "configlib" -include("ConfigLib-Core") -findProject(":ConfigLib-Core")?.name = "configlib-core" -include("ConfigLib-Bukkit") -findProject(":ConfigLib-Bukkit")?.name = "configlib-bukkit" -include("ConfigLib-Bungee") -findProject(":ConfigLib-Bungee")?.name = "configlib-bungee" - +include("configlib-core") +include("configlib-paper") +include("configlib-waterfall") +include("configlib-velocity")