diff --git a/.github/renovate.json b/.github/renovate.json new file mode 100644 index 00000000..28965bad --- /dev/null +++ b/.github/renovate.json @@ -0,0 +1,6 @@ +{ + "extends": [ + "config:base", + ":gitSignOff" + ] +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..50ea7865 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,43 @@ +name: build + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + java-version: [8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25] + steps: + - name: Checkout + uses: actions/checkout@v5 + - name: Set up Java ${{matrix.java-version}} + uses: actions/setup-java@v5 + with: + distribution: zulu + java-version: ${{matrix.java-version}} + - name: Run tests + run: ./mvnw --batch-mode --no-transfer-progress -e test + + coverage: + needs: test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + - name: Create code coverage report + run: ./mvnw --batch-mode --no-transfer-progress -e test jacoco:report + - name: Upload code coverage report + uses: codecov/codecov-action@v5 + with: + files: target/site/jacoco/jacoco.xml + name: codecov + + dependency-check: + needs: test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v5 + - name: Check dependencies + run: ./mvnw --batch-mode --no-transfer-progress -e dependency-check:check -DnvdApiKey='${{ secrets.NVD_API_KEY }}' -DossIndexUsername='${{ secrets.OSSINDEX_USERNAME }}' -DossIndexPassword='${{ secrets.OSSINDEX_TOKEN }}' diff --git a/.gitignore b/.gitignore index 62404111..37a81e09 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.iml .idea target +.mvn/wrapper/*.jar +.mvn/settings.xml \ No newline at end of file diff --git a/.mvn/owasp-suppressions.xml b/.mvn/owasp-suppressions.xml new file mode 100644 index 00000000..ddeb92a3 --- /dev/null +++ b/.mvn/owasp-suppressions.xml @@ -0,0 +1,10 @@ + + + + + ^pkg:maven/com\.google\.guava/guava@.*$ + CVE-2020-8908 + + diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..6a6b8b2c --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,17 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 0206b6fd..00000000 --- a/.travis.yml +++ /dev/null @@ -1,5 +0,0 @@ -language: java -install: "mvn install -DskipTests=true" -script: "mvn test -e" -jdk: - - oraclejdk7 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..5a1e67ca --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,65 @@ +# Contributing + +Contributions are very welcome! + +## How to contribute + +We love pull requests. Here is a quick guide: + +1. Fork the repo. +2. Create a new branch from `master`. +3. Add your change together with necessary tests. +4. Run `mvn clean verify` and ensure all tests are passing. +5. Commit with a DCO sign-off message (see next section) and push to your fork/branch. +6. Create a pull request. + +## Sign your work – the Developer's Certificate of Origin +The sign-off is a simple line at the end of the explanation for the patch, +which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch. +The rules are pretty simple: +You need to certify the below (from [developercertificate.org](https://developercertificate.org/)): + +> Developer's Certificate of Origin 1.1 +> +> By making a contribution to this project, I certify that: +> +> (a) The contribution was created in whole or in part by me and I +> have the right to submit it under the open source license +> indicated in the file; or +> +> (b) The contribution is based upon previous work that, to the best +> of my knowledge, is covered under an appropriate open source +> license and I have the right under that license to submit that +> work with modifications, whether created in whole or in part +> by me, under the same open source license (unless I am +> permitted to submit under a different license), as indicated +> in the file; or +> +> (c) The contribution was provided directly to me by some other +> person who certified (a), (b) or (c) and I have not modified +> it. +> +> (d) I understand and agree that this project and the contribution +> are public and that a record of the contribution (including all +> personal information I submit with it, including my sign-off) is +> maintained indefinitely and may be redistributed consistent with +> this project or the open source license(s) involved. + +#### DCO Sign-Off Methods + +The DCO requires a sign-off message in the following format appear on each commit in the pull request: + + Signed-off-by: Random J Developer + +using your real name (sorry, no pseudonyms or anonymous contributions.) + +The DCO text can either be manually added to your commit body, +or you can add either **`-s`** or **`--signoff`** to your usual **`git commit`** commands. +If you forget to add the sign-off you can also amend a previous commit with the sign-off +by running **`git commit --amend -s`**. +If you've pushed your changes to Github already +you'll need to force push your branch after this with **`git push -f`**. + +#### Alternative Sign-Off Methods in rare cases + +If it is really no option for you to disclose your real name and email address, there might be a chance that you can get your contribution accepted. In this case please contact the maintainers directly and verify the adherence to the DCO of the contribution manually. This might include quite some legal overhead for both parties. diff --git a/LICENSE-2.0.txt b/LICENSE similarity index 100% rename from LICENSE-2.0.txt rename to LICENSE diff --git a/LICENSE-2.0.html b/LICENSE-2.0.html deleted file mode 100644 index 4ff2e0a5..00000000 --- a/LICENSE-2.0.html +++ /dev/null @@ -1,427 +0,0 @@ - - - - Apache License, Version 2.0 - - - - - - - - - - - - - - - - - -
- - -
-

Apache License

Version 2.0, January 2004

-http://www.apache.org/licenses/

-

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

-

1. Definitions.

-

"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document.

-

"Licensor" shall mean the copyright owner or entity authorized by the -copyright owner that is granting the License.

-

"Legal Entity" shall mean the union of the acting entity and all other -entities that control, are controlled by, or are under common control with -that entity. For the purposes of this definition, "control" means (i) the -power, direct or indirect, to cause the direction or management of such -entity, whether by contract or otherwise, or (ii) ownership of fifty -percent (50%) or more of the outstanding shares, or (iii) beneficial -ownership of such entity.

-

"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License.

-

"Source" form shall mean the preferred form for making modifications, -including but not limited to software source code, documentation source, -and configuration files.

-

"Object" form shall mean any form resulting from mechanical transformation -or translation of a Source form, including but not limited to compiled -object code, generated documentation, and conversions to other media types.

-

"Work" shall mean the work of authorship, whether in Source or Object form, -made available under the License, as indicated by a copyright notice that -is included in or attached to the work (an example is provided in the -Appendix below).

-

"Derivative Works" shall mean any work, whether in Source or Object form, -that is based on (or derived from) the Work and for which the editorial -revisions, annotations, elaborations, or other modifications represent, as -a whole, an original work of authorship. For the purposes of this License, -Derivative Works shall not include works that remain separable from, or -merely link (or bind by name) to the interfaces of, the Work and Derivative -Works thereof.

-

"Contribution" shall mean any work of authorship, including the original -version of the Work and any modifications or additions to that Work or -Derivative Works thereof, that is intentionally submitted to Licensor for -inclusion in the Work by the copyright owner or by an individual or Legal -Entity authorized to submit on behalf of the copyright owner. For the -purposes of this definition, "submitted" means any form of electronic, -verbal, or written communication sent to the Licensor or its -representatives, including but not limited to communication on electronic -mailing lists, source code control systems, and issue tracking systems that -are managed by, or on behalf of, the Licensor for the purpose of discussing -and improving the Work, but excluding communication that is conspicuously -marked or otherwise designated in writing by the copyright owner as "Not a -Contribution."

-

"Contributor" shall mean Licensor and any individual or Legal Entity on -behalf of whom a Contribution has been received by Licensor and -subsequently incorporated within the Work.

-

2. Grant of Copyright License. Subject to the -terms and conditions of this License, each Contributor hereby grants to You -a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable -copyright license to reproduce, prepare Derivative Works of, publicly -display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form.

-

3. Grant of Patent License. Subject to the terms -and conditions of this License, each Contributor hereby grants to You a -perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable -(except as stated in this section) patent license to make, have made, use, -offer to sell, sell, import, and otherwise transfer the Work, where such -license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by -combination of their Contribution(s) with the Work to which such -Contribution(s) was submitted. If You institute patent litigation against -any entity (including a cross-claim or counterclaim in a lawsuit) alleging -that the Work or a Contribution incorporated within the Work constitutes -direct or contributory patent infringement, then any patent licenses -granted to You under this License for that Work shall terminate as of the -date such litigation is filed.

-

4. Redistribution. You may reproduce and -distribute copies of the Work or Derivative Works thereof in any medium, -with or without modifications, and in Source or Object form, provided that -You meet the following conditions:

-
    -
  1. You must give any other recipients of the Work or Derivative Works a -copy of this License; and
  2. - -
  3. You must cause any modified files to carry prominent notices stating -that You changed the files; and
  4. - -
  5. You must retain, in the Source form of any Derivative Works that You -distribute, all copyright, patent, trademark, and attribution notices from -the Source form of the Work, excluding those notices that do not pertain to -any part of the Derivative Works; and
  6. - -
  7. If the Work includes a "NOTICE" text file as part of its distribution, -then any Derivative Works that You distribute must include a readable copy -of the attribution notices contained within such NOTICE file, excluding -those notices that do not pertain to any part of the Derivative Works, in -at least one of the following places: within a NOTICE text file distributed -as part of the Derivative Works; within the Source form or documentation, -if provided along with the Derivative Works; or, within a display generated -by the Derivative Works, if and wherever such third-party notices normally -appear. The contents of the NOTICE file are for informational purposes only -and do not modify the License. You may add Your own attribution notices -within Derivative Works that You distribute, alongside or as an addendum to -the NOTICE text from the Work, provided that such additional attribution -notices cannot be construed as modifying the License. -
    -
    -You may add Your own copyright statement to Your modifications and may -provide additional or different license terms and conditions for use, -reproduction, or distribution of Your modifications, or for any such -Derivative Works as a whole, provided Your use, reproduction, and -distribution of the Work otherwise complies with the conditions stated in -this License. -
  8. - -
- -

5. Submission of Contributions. Unless You -explicitly state otherwise, any Contribution intentionally submitted for -inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the -terms of any separate license agreement you may have executed with Licensor -regarding such Contributions.

-

6. Trademarks. This License does not grant -permission to use the trade names, trademarks, service marks, or product -names of the Licensor, except as required for reasonable and customary use -in describing the origin of the Work and reproducing the content of the -NOTICE file.

-

7. Disclaimer of Warranty. Unless required by -applicable law or agreed to in writing, Licensor provides the Work (and -each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT -WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, -without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You -are solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise -of permissions under this License.

-

8. Limitation of Liability. In no event and -under no legal theory, whether in tort (including negligence), contract, or -otherwise, unless required by applicable law (such as deliberate and -grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, -incidental, or consequential damages of any character arising as a result -of this License or out of the use or inability to use the Work (including -but not limited to damages for loss of goodwill, work stoppage, computer -failure or malfunction, or any and all other commercial damages or losses), -even if such Contributor has been advised of the possibility of such -damages.

-

9. Accepting Warranty or Additional Liability. -While redistributing the Work or Derivative Works thereof, You may choose -to offer, and charge a fee for, acceptance of support, warranty, indemnity, -or other liability obligations and/or rights consistent with this License. -However, in accepting such obligations, You may act only on Your own behalf -and on Your sole responsibility, not on behalf of any other Contributor, -and only if You agree to indemnify, defend, and hold each Contributor -harmless for any liability incurred by, or claims asserted against, such -Contributor by reason of your accepting any such warranty or additional -liability.

-

END OF TERMS AND CONDITIONS

-

APPENDIX: How to apply the Apache License to your work

-

To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included -on the same "printed page" as the copyright notice for easier -identification within third-party archives.

-
   :::text
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-
-
-
- - - - diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..5910ca24 --- /dev/null +++ b/NOTICE @@ -0,0 +1,16 @@ + +config-builder +-------------- + +Copyright 2013-2018 Matthias Bollwein (matthias.bollwein@tngtech.com) +Copyright 2013-2018 Andreas Würl (andreas.wuerl@tngtech.com) +Copyright 2013-2018 Thomas Endres (thomas.endres@tngtech.com) +Copyright 2013-2014 Andreas Schmid (andreas.schmid@tngtech.com) +Copyright 2013-2014 Jan Schäfer (jan.schaefer@tngtech.com) +Copyright 2013-2015 Fabian Winter (fabian.winter@tngtech.com) +Copyright 2014 Andre Wallat +Copyright 2017-2020 Manfred Hanke (manfred.hanke@tngtech.com) +Copyright 2018 Johannes Najjar (johannes.najjar@tngtech.com) +Copyright 2020 Daniel Kraschewski (daniel.kraschewski@tngtech.com) + +This product includes software developed at TNG Technology Consulting GmbH (https://www.tngtech.com/). \ No newline at end of file diff --git a/README.md b/README.md index 6b1a9be4..51bf392b 100644 --- a/README.md +++ b/README.md @@ -1,84 +1,86 @@ -Java Config-Builder [![Build Status](https://travis-ci.org/TNG/config-builder.png?branch=master)](https://travis-ci.org/TNG/config-builder) -================== +Java Config-Builder + [![Build Status](https://github.com/TNG/config-builder/actions/workflows/build.yml/badge.svg)](https://github.com/TNG/config-builder/actions/workflows/build.yml?query=branch%3Amaster) + [![Coverage Status](https://codecov.io/gh/TNG/config-builder/branch/master/graphs/badge.svg)](https://app.codecov.io/gh/TNG/config-builder) + [![Apache License 2.0](https://img.shields.io/badge/license-apache2-red.svg?style=flat-square)](http://opensource.org/licenses/Apache-2.0) + [![Maven Central](https://img.shields.io/maven-central/v/com.tngtech.java/config-builder.svg?style=flat-square)](https://maven-badges.herokuapp.com/maven-central/com.tngtech.java/config-builder) + [![Javadocs](https://javadoc.io/badge/com.tngtech.java/config-builder.svg?color=brightgreen)](https://javadoc.io/doc/com.tngtech.java/config-builder) +=================== #### Table of Contents -[What It Is](#what-is-it) -[Motivation](#motivation) +[What It Is](#what-it-is) +[Motivation](#motivation) [How To Build Your Config](#how-to-build-your-config) -[How To Merge With An Existing Config](#how-to-merge-with-an-existing-config) +[How To Import An Existing Config](#how-to-import-an-existing-config) [Usage example](#usage-example) +[Presentation](#presentation) [Java Doc](#java-doc) What It Is ---------- +The Config-Builder makes use of annotations and reflections in order to build configured instances of custom classes. -The ConfigBuilder makes use of annotations and reflections in order to build configured instances of custom classes. - -Its features include +Its features include 1. defining default values and loading of values from properties files, system properties, the command line and others 2. configuring of not only String values, but fields of arbitrary types -3. configuring of collection fields -4. merging configs -5. JSR303 validation of the instances it builds. +3. configuring of collection fields +4. merging configs +5. [JSR380](https://www.jcp.org/en/jsr/detail?id=380) validation of the instances it builds. Motivation ---------- - -Many Java Projects include one or more classes that store configuration values and objects. Often, these come from +Many Java projects include one or more classes that store configuration values and objects. These often come from properties files, system properties and environment variables or command line arguments, which requires the developer to implement the finding and loading of files, parsing the values etc. for every new project. -This is is a time-consuming process, so why not spare this time and get started much faster? Although there are libraries -that implement the loading of properties files and some possibilities of building configured objects e.g. in Spring, +This is a time-consuming process, so why not spare this time and get started much faster? Although there are libraries +that implement the loading of properties files and some possibilities of building configured objects e.g. in Spring, there hasn't been a really easy-yet-powerful solution so far. -This is where the Config Builder comes in. It doesn't require any additional classes besides the config itself. -Instead of manually implementing the loading of values from files etc., building a config can now be easily done +This is where the Config-Builder comes in. It doesn't require any additional classes besides the config itself. +Instead of manually implementing the loading of values from files etc., building a config can now be easily done by using annotations. How To Build Your Config ------------------------ - -####1. Create your class: +#### 1. Create your class: ```java public class Config { private String someNumber; private Collection stringCollection; - ... + // ... } ``` -####2. Annotate the class (configure the loading of properties files) -If you want the ConfigBuilder to get values from properties files, -you can specify the files' basenames (no file extension or path) by -annotating your config class with the @PropertiesFiles annotation. -You can specify multiple basenames like this: +#### 2. Annotate the class (configure the loading of properties files) +If you want the Config-Builder to get values from properties files, +you can specify the files' basenames (no file extension or path) by +annotating your config class with the `@PropertiesFiles` annotation. +You can specify multiple basenames like this: ```java @PropertiesFiles({file1,file2,...}) ``` -By default, properties files are loaded using the PropertyLoader's default config, which +By default, properties files are loaded using the `PropertyLoader`'s default config, which searches for files in the current directory, the ContextClassLoader and the user's home directory. -You can manually specify the search locations by annotating your config class with the @PropertyLocations annotation, e.g. +You can manually specify the search locations by annotating your config class with the `@PropertyLocations` annotation, e.g. ```java @PropertyLocations(directories = {"/home/user"}, resourcesForClasses={MyApp.class}, contextClassLoader = true) ``` -The PropertyLoader also searches for files with the default suffixes, i.e. the user name, local host names and 'override'. -You can manually set the suffixes by annotating your config class with the @PropertySuffixes annotation like this: +The `PropertyLoader` also searches for files with the default suffixes, i.e. the user name, local host names and 'override'. +You can manually set the suffixes by annotating your config class with the `@PropertySuffixes` annotation like this: ```java @PropertySuffixes(extraSuffixes = {"tngtech","myname"}, hostNames = true) ``` -The default file extensions are .properties and .xml. You can replace the .properties file extension with your own -by annotating your config class with +The default file extensions are `.properties` and `.xml`. You can replace the .properties file extension with your own +by annotating your config class with ```java @PropertyExtension("fileextension") ``` -####3. Annotate the fields - -#####3.1 Get the String value +#### 3. Annotate the fields +##### 3.1 Get the String value There are five annotations that specify where the String value that configures a field comes from: ```java @DefaultValue("value") @@ -89,61 +91,63 @@ There are five annotations that specify where the String value that configures a ``` By default, when parsing the annotations, priority is as above, i.e. any value found on the command line overwrites a value found in properties, which in turn overwrites the environment variable value and so on. -This order can be customized, see [4.](#4-change-the-order-in-which-annotations-are-processed-and-use-your-own-error-messages). +This order can be customized, see [§5 (Change the order in which annotations are processed)](#5-change-the-order-in-which-annotations-are-processed). -#####3.2 Transform it to any object or a collection +##### 3.2 Transform it to any object or a collection Fields don't have to be Strings. You can configure collection fields or even any type you wish (or a collection of that type). Some simple transformers are included and used by default, e.g. a String will automatically be converted to an integer, a -boolean value or even a collection as needed. +boolean value, an enum value, or even a collection as needed. + +If you need more complex transformers, you can also implement your own by extending the `TypeTransformer` class, and specifying them in the `@TypeTransformers` annotation. -If you need more complex transformers, you can also implement your own by extending the TypeTransformer class, and specifying them in the ```@TypeTransformers``` annotation. - -Finally, the original value may not always be a String. To support this case, the annotation takes a list of possible transformers, and the one with the right -source and target types is automatically detected and used. - -####4. Add JSR validation annotations and/or define a custom validation method +Finally, the original value may not always be a String. To support this case, the annotation takes a list of possible transformers, and the one with the right +source and target types is automatically detected and used. +##### 3.3 Prevent sensitive data from being logged +Resolved values are usually logged at debug level. +If this is not desired, just add the `@DoNotLogValue` annotation to the field containing your sensitive data. + +#### 4. Add JSR validation annotations and/or define a custom validation method After an instance of your config is built, it is automatically validated. You can either use JSR validation annotations -(@NotNull,...) or define a custom validation method: +(`@NotNull`, ...) or define a custom validation method: ```java @Validation private void validate() { - <...> + // ... } ``` -####5. Change the order in which annotations are processed and use your own error messages - +#### 5. Change the order in which annotations are processed You can change the order in which annotations are processed globally or individually for each field. -To specify a global order for parsing ValueExtractorAnnotation annotations, annotate the class with the -@LoadingOrder annotation. To change the order for a certain field, annotate the field. -The order may only contain ValueExtractorAnnotations, i.e. -CommandLineValue.class, PropertyValue.class and DefaultValue.class. Example: +To specify a global order for parsing `ValueExtractorAnnotation` annotations, annotate the class with the +`@LoadingOrder` annotation. To change the order for a certain field, annotate the field. +The `@LoadingOrder` may only contain the following values: ```java -@LoadingOrder({PropertyValue.class, EnvironmentVariableValue.class, SystemPropertyValue.class, CommandLineValue.class, DefaultValue.class}) +@LoadingOrder({PropertyValue.class, EnvironmentVariableValue.class, SystemPropertyValue.class, CommandLineValue.class, ImportedValue.class, DefaultValue.class}) ``` -To specify your own error messages file (which is loaded by the PropertyLoader with the same settings as other the properties files), -annotate the class with the @ErrorMessageFile annotation: +#### 6. Use your own error messages +To specify your own error messages file (which is loaded by the `PropertyLoader` with the same settings as other the properties files), +annotate the class with the `@ErrorMessageFile` annotation: ```java @ErrorMessageFile("myErrorMessages") ``` -####6. Build an instance of your class +#### 7. Build an instance of your class ```java -Config myConfig = new ConfigBuilder(Config.class).withCommandLineArgs(args).build(); +Config myConfig = ConfigBuilder.on(Config.class).withCommandLineArgs(args).build(); ``` -How To Merge With An Existing Config ------------------------------------- -If you already have an instance of your config class and want to only configure the fields which are not null, use +How To Import An Existing Config +-------------------------------- +If you already have an instance of your config class and want to only configure the fields which are not `null`, use ```java -newConfig = new ConfigBuilder(Config.class).withCommandLineArgs(args).merge(existingConfig); +Config newConfig = ConfigBuilder.on(Config.class).withCommandLineArgs(args).withImportedConfiguration(existingConfig).build(); ``` -Note that primitive type fields are always overwritten! -Since primitive types can not be checked for 'null', it is not possible to check whether primitive fields of an existing config +Note that primitive type fields are always overwritten! +Since primitive types can not be checked for `null`, it is not possible to check whether primitive fields of an existing config have already been set. Hence, for the moment, primitives are always overwritten. Usage example @@ -155,56 +159,58 @@ Say you have a config that looks like this: @PropertySuffixes(extraSuffixes = {"tngtech","myname"}, hostNames = true) public class Config { - public static class StringToPidFixTransformer implements TypeTransformer { + public static class StringToPidFixTransformer extends TypeTransformer { @Override public PidFix transform(String input) { - <...> + // ... } } - - @DefaultValue("false") // values are automatically be converted to primitive types + + // default false, true if the option is present @CommandLineValue(shortOpt="t", longOpt="test", hasArg=false) // this is a flag argument private boolean runInTestMode; - - @DefaultValue("3") + + @DefaultValue("3") // values are automatically be converted to primitive types @CommandLineValue(shortOpt="rl", longOpt="runLevel", hasArg=true) private int runLevel; - + @EnvironmentVariableValue("PATH") @PropertyValue("path") // maps to the key "path" in the properties file private String path; - + @SystemPropertyValue("user.name") // maps to the field "user.name" in the system properties @NotEmpty("username.notEmpty") // JSR-303 validation (Field should not be empty) private String userName; - - @TypeTransformer(StringToPidFixTransformer.class) + + @TypeTransformers(StringToPidFixTransformer.class) @CommandLineValue(shortOpt="pc", longOpt="pidFixCollection", hasArg=true) private Collection pidFixCollection; - - @TypeTransformer(StringToPidFixTransformer.class) + + @TypeTransformers(StringToPidFixTransformer.class) @CommandLineValue(shortOpt="p", longOpt="pidFix", hasArg=true) private PidFix pidFix; - + @Validation private void validate() { - <...> + // ... } - ... + + //... } ``` To build a configured instance, simply call ```java -Config myConfig = new ConfigBuilder(Config.class).withCommandLineArgs(args).build(); +Config myConfig = ConfigBuilder.on(Config.class).withCommandLineArgs(args).build(); ``` Presentation --------- - +------------ A sample presentation can be found at http://tng.github.io/config-builder - Java Doc -------- - Full javadoc of the code can be found here http://tng.github.io/config-builder/javadoc + +How to contribute +----------------- +Please have a look at [CONTRIBUTING.md](CONTRIBUTING.md) diff --git a/mvnw b/mvnw new file mode 100755 index 00000000..08303327 --- /dev/null +++ b/mvnw @@ -0,0 +1,250 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.0 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl="${value-}" ;; + distributionSha256Sum) distributionSha256Sum="${value-}" ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_HOME="$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( new java.net.URL( args[0] ).openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 00000000..33cbf988 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,146 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.0 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml index f8ee0683..9ba7fc27 100644 --- a/pom.xml +++ b/pom.xml @@ -4,28 +4,36 @@ 4.0.0 com.tngtech.java config-builder - 1.1-SNAPSHOT + 1.8.2-SNAPSHOT jar Config-Builder The Config Builder creates fully configured instances of config classes, using values from various sources like properties files, command line arguments etc. https://github.com/TNG/config-builder + The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt + https://www.apache.org/licenses/LICENSE-2.0.txt repo + git@github.com:TNG/config-builder.git - scm:git:git@github.com:TNG/config-builder.git - scm:git:git@github.com:TNG/config-builder.git - HEAD - + scm:git:https://github.com/TNG/config-builder.git + scm:git:https://github.com/TNG/config-builder.git + HEAD + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + Matthias Bollwein - matthias.bollwein@tngtech.com TNG Technology Consulting @@ -35,36 +43,113 @@ Andreas Würl - andreas.wuerl@tngtech.com + TNG Technology Consulting + + + Manfred Hanke + manfred.hanke@tngtech.com + TNG Technology Consulting + + + Jan Thiart + jan.thiart@tngtech.com TNG Technology Consulting TNG Technology Consulting - http://www.tngtech.com/ + https://www.tngtech.com/ - - org.sonatype.oss - oss-parent - 7 - + + + UTF-8 + UTF-8 + github + 1.8 + + + 1.10.0 + 33.5.0-jre + 6.2.5.Final + 3.0.4 + 1.5.1 + 0.10.2 + 2.0.17 + + 3.27.6 + 5.13.4 + 4.11.0 + + + 3.1.1 + 1.7.0 + 3.14.1 + 0.8.13 + 3.5.4 + 12.1.6 + 3.3.1 + 3.12.0 + 3.2.8 + + + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + true + sonatype-release + deploy + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${nexus-staging-maven-plugin.version} + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins maven-compiler-plugin - 2.3.2 + ${maven-compiler-plugin.version} - 1.7 - 1.7 + ${java.version} + ${java.version} + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + prepare-agent + + prepare-agent + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + org.apache.maven.plugins maven-source-plugin - 2.2.1 + ${maven-source-plugin.version} attach-sources @@ -78,28 +163,44 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + ${maven-javadoc-plugin.version} attach-javadocs jar + + ${java.version} + + + + + org.owasp + dependency-check-maven + ${dependency-check-maven.version} + + 5 + .mvn/owasp-suppressions.xml + + - signing + + sonatype-release org.apache.maven.plugins maven-gpg-plugin - 1.4 + ${maven-gpg-plugin.version} sign-artifacts @@ -116,72 +217,68 @@ - + - com.tngtech.java - property-loader - 1.1 + commons-cli + commons-cli + ${commons-cli.version} - - junit - junit - 4.11 - test + com.google.guava + guava + ${guava.version} - org.mockito - mockito-all - 1.9.5 - test + org.hibernate.validator + hibernate-validator + ${hibernate-validator.version} - org.unitils - unitils-core - 3.3 - test + org.glassfish + jakarta.el + ${jakarta-el.version} - com.google.guava - guava - 15.0 + com.tngtech.java + property-loader + ${property-loader.version} org.reflections reflections - 0.9.8 - - - cglib - cglib - 2.2.2 + ${reflections.version} - commons-cli - commons-cli - 1.2 + org.slf4j + slf4j-api + ${slf4j.version} + + - javax.validation - validation-api - 1.0.0.GA + org.assertj + assertj-core + ${assertj-core.version} + test - log4j - log4j - 1.2.17 + org.junit.jupiter + junit-jupiter + ${junit-jupiter.version} + test - org.hibernate - hibernate-validator-annotation-processor - 4.1.0.Final + org.mockito + mockito-junit-jupiter + ${mockito-junit-jupiter.version} + test org.slf4j - slf4j-log4j12 - 1.5.2 + slf4j-reload4j + ${slf4j.version} + test - diff --git a/src/main/java/com/tngtech/configbuilder/ConfigBuilder.java b/src/main/java/com/tngtech/configbuilder/ConfigBuilder.java index 6f2cb89b..27d9597c 100644 --- a/src/main/java/com/tngtech/configbuilder/ConfigBuilder.java +++ b/src/main/java/com/tngtech/configbuilder/ConfigBuilder.java @@ -1,62 +1,69 @@ package com.tngtech.configbuilder; import com.tngtech.configbuilder.annotation.configuration.LoadingOrder; +import com.tngtech.configbuilder.annotation.configuration.PropertyNamePrefix; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.ErrorMessageFile; import com.tngtech.configbuilder.configuration.BuilderConfiguration; import com.tngtech.configbuilder.configuration.ErrorMessageSetup; -import com.tngtech.configbuilder.util.ConfigBuilderFactory; import com.tngtech.configbuilder.util.*; import com.tngtech.propertyloader.PropertyLoader; +import com.tngtech.propertyloader.impl.DefaultPropertyFilterContainer; +import com.tngtech.propertyloader.impl.DefaultPropertyLocationContainer; +import com.tngtech.propertyloader.impl.DefaultPropertySuffixContainer; +import com.tngtech.propertyloader.impl.interfaces.PropertyLoaderFilter; import org.apache.commons.cli.HelpFormatter; -import org.apache.commons.cli.Options; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Arrays; import java.util.List; +import java.util.Properties; /** * Builds a config object. * ConfigBuilder instantiates a class and sets fields of the instance by parsing annotations and * loading values from properties files or the command line. It validates the instance by parsing JSR303 constraint annotations.

- *

+ * * Fields of the config class can have the following annotations:
* {@link com.tngtech.configbuilder.annotation.valueextractor.DefaultValue}
* {@link com.tngtech.configbuilder.annotation.valueextractor.PropertyValue}
* {@link com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue}
* {@link com.tngtech.configbuilder.annotation.valueextractor.SystemPropertyValue}
* {@link com.tngtech.configbuilder.annotation.valueextractor.EnvironmentVariableValue}
- * {@link LoadingOrder}

- *

+ * {@link LoadingOrder}
+ * * Properties files are loaded with a PropertyLoader using its default config. In order to change settings for the PropertyLoader, the config class may be annotated with
* {@link com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertiesFiles}
* {@link com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyLocations}
* {@link com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertySuffixes}
- * {@link com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyExtension}

- *

+ * {@link com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyExtension}
+ * * To specify a global order for parsing {@link com.tngtech.configbuilder.annotation.valueextractor.ValueExtractorAnnotation} annotations, annotate the class with
- * {@link LoadingOrder}

- *

+ * {@link LoadingOrder}
+ * * To specify your own error messages file (which is loaded by the PropertyLoader with the same settings as other the properties files), annotate the class with
- * {@link ErrorMessageFile}

+ * {@link ErrorMessageFile}
* * @param The type of the config class which shall be instantiated. * @author Matthias Bollwein * @version 0.1-SNAPSHOT */ public class ConfigBuilder { - + private final static Logger LOGGER = LoggerFactory.getLogger(CommandLineHelper.class); + public static final Object AT_CONTEXT_CLASS_PATH = new Object(); + private final BuilderConfiguration builderConfiguration; private final CommandLineHelper commandLineHelper; private final FieldSetter fieldSetter; private final ConfigValidator configValidator; private final ErrorMessageSetup errorMessageSetup; private final ConstructionHelper constructionHelper; - - private Class configClass; - private Options commandLineOptions; - private PropertyLoader propertyLoader; + private final Class configClass; + private final PropertyLoader propertyLoader; + private final Properties additionalProperties; private String[] commandLineArgs = {}; - + protected ConfigBuilder(Class configClass, ConfigBuilderFactory configBuilderFactory) { - configBuilderFactory.initialize(); this.configClass = configClass; this.builderConfiguration = configBuilderFactory.getInstance(BuilderConfiguration.class); @@ -65,16 +72,15 @@ protected ConfigBuilder(Class configClass, ConfigBuilderFactory configBuilder this.fieldSetter = configBuilderFactory.getInstance(FieldSetter.class); this.errorMessageSetup = configBuilderFactory.getInstance(ErrorMessageSetup.class); this.constructionHelper = configBuilderFactory.getInstance(ConstructionHelper.class); - - propertyLoader = configBuilderFactory.getInstance(PropertyLoaderConfigurator.class).configurePropertyLoader(configClass); - commandLineOptions = commandLineHelper.getOptions(configClass); + this.additionalProperties = configBuilderFactory.createInstance(Properties.class); + this.propertyLoader = configBuilderFactory.getInstance(PropertyLoaderConfigurator.class).configurePropertyLoader(configClass); } /** * @param configClass The config class of which an instance shall be built. */ public ConfigBuilder(Class configClass) { - this(configClass,new ConfigBuilderFactory()); + this(configClass, new ConfigBuilderFactory()); } /** @@ -91,8 +97,8 @@ public ConfigBuilder withCommandLineArgs(String[] args) { /** * Imports the values from the given object according to the field names in the annotations - * @param importedConfiguration - * @return + * @param importedConfiguration configuration object to be imported + * @return the instance of ConfigBuilder */ public ConfigBuilder withImportedConfiguration(Object importedConfiguration) { builderConfiguration.setImportedConfiguration(importedConfiguration); @@ -100,23 +106,140 @@ public ConfigBuilder withImportedConfiguration(Object importedConfiguration) } /** - * Configures the Config Builder to load given properties files instead of those specified in the config class. + * Configures the Config Builder to load given property files instead of those specified in the config class. * - * @param baseNames - * @return + * @param baseNames base names of the property files to be loaded + * @return the instance of ConfigBuilder */ public ConfigBuilder overridePropertiesFiles(List baseNames) { propertyLoader.withBaseNames(baseNames); return this; } + /** + * Provide additional properties which will overwrite the properties retrieved by the property loader + * + * @param properties to be added to the properties already present (starting from the result of the property loader) + * @return the instance of ConfigBuilder + */ + public ConfigBuilder addProperties(Properties properties) { + additionalProperties.putAll(properties); + return this; + } + + /** + * set the extension to search for property files + * @param propertyExtension property file name extension to use + * @return the instance of ConfigBuilder + */ + public ConfigBuilder withPropertyExtension(String propertyExtension) { + propertyLoader.withExtension(propertyExtension); + return this; + } + + /** + * set property suffix to b + * @param propertySuffix property file name suffix + * @return the instance of ConfigBuilder + */ + public ConfigBuilder withPropertySuffix(String propertySuffix) { + return withPropertySuffixes(propertySuffix); + } + + /** + * replace list of possible property suffixes by given elements + * @param suffixArray one or more property file name suffix + * @return the instance of ConfigBuilder + */ + public ConfigBuilder withPropertySuffixes(String ... suffixArray) { + final DefaultPropertySuffixContainer suffixes = propertyLoader.getSuffixes(); + suffixes.clear(); + suffixes.addSuffixList(Arrays.asList(suffixArray)); + return this; + } + + /** + * add more property file suffixes to the list of possible property suffixes + * @param suffixArray one or more property file name suffix + * @return the instance of ConfigBuilder + */ + public ConfigBuilder addPropertySuffixes(String... suffixArray) { + propertyLoader.getSuffixes().addSuffixList(Arrays.asList(suffixArray)); + return this; + } + + /** + * set file name of property file to read + * @param fileName property file name + * @return the instance of ConfigBuilder + */ + public ConfigBuilder withPropertiesFile(String fileName) { + return withPropertiesFiles(fileName); + } + + /** + * set file names of property files to read + * @param fileNames one or more property file names + * @return the instance of ConfigBuilder + */ + public ConfigBuilder withPropertiesFiles(String ... fileNames) { + propertyLoader.withBaseNames(Arrays.asList(fileNames)); + return this; + } + + /** + * set property locations + * @param propertyLocations lists of property locations which can be + * Strings: use as Directory + * Classes: use as Class Resource Location + * @return the instance of ConfigBuilder + */ + public ConfigBuilder withPropertyLocations(Object ... propertyLocations) { + final DefaultPropertyLocationContainer locations = propertyLoader.getLocations(); + locations.clear(); + for (Object propertyLocation : propertyLocations) { + if (propertyLocation instanceof String) { + locations.atDirectory((String)propertyLocation); + } else if (propertyLocation instanceof Class) { + locations.atRelativeToClass((Class)propertyLocation); + } else if (propertyLocation == AT_CONTEXT_CLASS_PATH) { + locations.atContextClassPath(); + } else { + LOGGER.warn("unhandled property location '{}'", propertyLocation); + } + } + return this; + } + + /** + * set property filters in use + * @param propertyFilters property filters which should be applied after loading properties + * @return the instance of ConfigBuilder + */ + @SafeVarargs + public final ConfigBuilder withPropertyFilters(Class... propertyFilters) { + final DefaultPropertyFilterContainer filterContainer = propertyLoader.getFilters(); + final List filters = filterContainer.getFilters(); + filters.clear(); + + for (Class propertyFilter : propertyFilters) { + try { + filters.add(propertyFilter.newInstance()); + } catch (InstantiationException | IllegalAccessException e) { + LOGGER.error("could not create filter '{}'", propertyFilter.getSimpleName(), e); + } + } + return this; + } + /** * Prints a help message for all command line options that are configured in the config class. */ public void printCommandLineHelp() { + initializeErrorMessageSetup(propertyLoader); HelpFormatter formatter = new HelpFormatter(); formatter.setSyntaxPrefix("Command Line Options for class " + configClass.getSimpleName() + ":"); - formatter.printHelp(" ", commandLineOptions); + formatter.printHelp(" ", commandLineHelper.getOptions(configClass)); } /** @@ -141,7 +264,15 @@ private void setupBuilderConfiguration(PropertyLoader propertyLoader) { if (configClass.isAnnotationPresent(LoadingOrder.class)) { builderConfiguration.setAnnotationOrder(configClass.getAnnotation(LoadingOrder.class).value()); } - builderConfiguration.setProperties(propertyLoader.load()); + + if (configClass.isAnnotationPresent(PropertyNamePrefix.class)) { + builderConfiguration.setPropertyNamePrefixes(configClass.getAnnotation(PropertyNamePrefix.class).value()); + } + + final Properties properties = propertyLoader.load(); + properties.putAll(additionalProperties); + builderConfiguration.setProperties(properties); + builderConfiguration.setCommandLine(commandLineHelper.getCommandLine(configClass, commandLineArgs)); } @@ -149,4 +280,15 @@ private void initializeErrorMessageSetup(PropertyLoader propertyLoader) { String errorMessageFile = configClass.isAnnotationPresent(ErrorMessageFile.class) ? configClass.getAnnotation(ErrorMessageFile.class).value() : null; errorMessageSetup.initialize(errorMessageFile, propertyLoader); } + + /** + * Gets an instance of the ConfigBuilder for a given config class + * + * @param clazz config class for which the config builder is instantiated. + * @param generic type of the config class + * @return ConfigBuilder instance for config class + */ + public static ConfigBuilder on(Class clazz) { + return new ConfigBuilder<>(clazz); + } } \ No newline at end of file diff --git a/src/main/java/com/tngtech/configbuilder/annotation/configuration/DoNotLogValue.java b/src/main/java/com/tngtech/configbuilder/annotation/configuration/DoNotLogValue.java new file mode 100644 index 00000000..5997e9fb --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/annotation/configuration/DoNotLogValue.java @@ -0,0 +1,17 @@ +package com.tngtech.configbuilder.annotation.configuration; + +import com.tngtech.configbuilder.ConfigBuilder; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation can be used to prevent a field's value from being logged + * when {@link ConfigBuilder#build(Object...)} builds a config instance. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface DoNotLogValue { +} diff --git a/src/main/java/com/tngtech/configbuilder/annotation/configuration/LoadingOrder.java b/src/main/java/com/tngtech/configbuilder/annotation/configuration/LoadingOrder.java index 62e7a0da..b30ce509 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/configuration/LoadingOrder.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/configuration/LoadingOrder.java @@ -2,20 +2,25 @@ import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; import com.tngtech.configbuilder.annotation.valueextractor.DefaultValue; +import com.tngtech.configbuilder.annotation.valueextractor.EnvironmentVariableValue; +import com.tngtech.configbuilder.annotation.valueextractor.ImportedValue; import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; +import com.tngtech.configbuilder.annotation.valueextractor.SystemPropertyValue; +import com.tngtech.configbuilder.annotation.valueextractor.ValueExtractorAnnotation; import java.lang.annotation.*; /** - * This annotation is used to specify the order in which the annotations CommandLineValue, PropertyValue and DefaultValue are processed. + * This annotation is used to specify the order in which the {@link ValueExtractorAnnotation} annotations + * {@link CommandLineValue}, {@link PropertyValue}, {@link EnvironmentVariableValue}, + * {@link SystemPropertyValue}, {@link ImportedValue} and {@link DefaultValue} are processed. * It can specify the order for a certain field if placed on the field, or a global order if placed on the config class. * The annotations are processed top-down until a string value is found, i.e. the order is from the most important to the least important. - * The order may only contain ValueExtractorAnnotations, i.e. CommandLineValue.class, PropertyValue.class and DefaultValue.class.
+ * The {@code LoadingOrder} may only contain the aforementioned {@link ValueExtractorAnnotation} classes.
* Usage: @LoadingOrder(value = {PropertyValue.class, CommandLineValue.class, DefaultValue.class}) */ @Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface LoadingOrder { - public Class[] value() default {CommandLineValue.class, PropertyValue.class, DefaultValue.class}; - + Class[] value() default {CommandLineValue.class, PropertyValue.class, DefaultValue.class}; } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/configuration/PropertyNamePrefix.java b/src/main/java/com/tngtech/configbuilder/annotation/configuration/PropertyNamePrefix.java new file mode 100644 index 00000000..fe47bbdc --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/annotation/configuration/PropertyNamePrefix.java @@ -0,0 +1,13 @@ +package com.tngtech.configbuilder.annotation.configuration; + +import java.lang.annotation.*; + +/** + * This annotation is used to specify additional prefixes to be included in the search for property names + * Usage: @PropertyNamePrefix(value = {"test.prefix."}) + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface PropertyNamePrefix { + String[] value() default {""}; +} diff --git a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyExtension.java b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyExtension.java index e6514f75..f3287eb0 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyExtension.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyExtension.java @@ -13,5 +13,5 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface PropertyExtension { - public String value(); + String value(); } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyFilters.java b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyFilters.java new file mode 100644 index 00000000..c6aa1e12 --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyFilters.java @@ -0,0 +1,19 @@ +package com.tngtech.configbuilder.annotation.propertyloaderconfiguration; + +import com.tngtech.propertyloader.impl.interfaces.PropertyLoaderFilter; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is used to specify the filters which the PropertyLoader applies to the properties files.
+ * Usage: @PropertyFilters({Config.class}) + */ +@PropertyLoaderConfigurationAnnotation(PropertyFiltersProcessor.class) +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface PropertyFilters { + Class[] value() default {}; +} diff --git a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyFiltersProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyFiltersProcessor.java new file mode 100644 index 00000000..e1ca694a --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyFiltersProcessor.java @@ -0,0 +1,46 @@ +package com.tngtech.configbuilder.annotation.propertyloaderconfiguration; + +import com.google.common.collect.Maps; +import com.tngtech.propertyloader.PropertyLoader; +import com.tngtech.propertyloader.impl.DefaultPropertyFilterContainer; +import com.tngtech.propertyloader.impl.filters.DecryptingFilter; +import com.tngtech.propertyloader.impl.filters.EnvironmentResolvingFilter; +import com.tngtech.propertyloader.impl.filters.ThrowIfPropertyHasToBeDefined; +import com.tngtech.propertyloader.impl.filters.VariableResolvingFilter; +import com.tngtech.propertyloader.impl.filters.WarnOnSurroundingWhitespace; +import com.tngtech.propertyloader.impl.interfaces.PropertyLoaderFilter; + +import java.lang.annotation.Annotation; +import java.util.Map; + +public class PropertyFiltersProcessor implements PropertyLoaderConfigurationProcessor { + + private interface Action { + void execute(); + } + + public void configurePropertyLoader( Annotation annotation, PropertyLoader propertyLoader ) { + DefaultPropertyFilterContainer filterContainer = propertyLoader.getFilters(); + Map, Action> classActionMap = createFilterMap( filterContainer ); + + filterContainer.clear(); + Class[] filters = ((PropertyFilters) annotation).value(); + for ( Class filter : filters ) { + if ( classActionMap.containsKey( filter ) ) { + classActionMap.get( filter ).execute(); + } else { + throw new IllegalStateException( "unhandled filter class " + filter.getSimpleName() ); + } + } + } + + private Map, Action> createFilterMap(DefaultPropertyFilterContainer filterContainer ) { + Map, Action> actionMap = Maps.newHashMap(); + actionMap.put(VariableResolvingFilter.class, filterContainer::withVariableResolvingFilter); + actionMap.put(DecryptingFilter.class, filterContainer::withDecryptingFilter); + actionMap.put(EnvironmentResolvingFilter.class, filterContainer::withEnvironmentResolvingFilter); + actionMap.put(WarnOnSurroundingWhitespace.class, filterContainer::withWarnOnSurroundingWhitespace); + actionMap.put(ThrowIfPropertyHasToBeDefined.class, filterContainer::withWarnIfPropertyHasToBeDefined); + return actionMap; + } +} diff --git a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLoaderConfigurationProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLoaderConfigurationProcessor.java index 6b3c60b4..b654db51 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLoaderConfigurationProcessor.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLoaderConfigurationProcessor.java @@ -5,5 +5,5 @@ import java.lang.annotation.Annotation; public interface PropertyLoaderConfigurationProcessor { - public void configurePropertyLoader(Annotation annotation, PropertyLoader propertyLoader); + void configurePropertyLoader(Annotation annotation, PropertyLoader propertyLoader); } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocations.java b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocations.java index 20094685..288ade96 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocations.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocations.java @@ -13,9 +13,9 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface PropertyLocations { - public String[] directories() default {}; + String[] directories() default {}; - public Class[] resourcesForClasses() default {}; + Class[] resourcesForClasses() default {}; - public boolean fromClassLoader() default false; + boolean fromClassLoader() default false; } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocationsProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocationsProcessor.java index 5b142785..cb8d0757 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocationsProcessor.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocationsProcessor.java @@ -14,7 +14,7 @@ public void configurePropertyLoader(Annotation annotation, PropertyLoader proper propertyLoader.atDirectory(location); } Class[] classes = ((PropertyLocations) annotation).resourcesForClasses(); - for (Class clazz : classes) { + for (Class clazz : classes) { propertyLoader.atRelativeToClass(clazz); } if (((PropertyLocations) annotation).fromClassLoader()) { diff --git a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertySuffixes.java b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertySuffixes.java index dd83656d..3a339d76 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertySuffixes.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertySuffixes.java @@ -13,8 +13,7 @@ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface PropertySuffixes { - public String[] extraSuffixes() default {}; - - public boolean hostNames() default false; + String[] extraSuffixes() default {}; + boolean hostNames() default false; } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringListTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringListTransformer.java index 9ad89a6e..ad280fa4 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringListTransformer.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringListTransformer.java @@ -3,18 +3,11 @@ import com.google.common.collect.Lists; import java.util.ArrayList; -import java.util.Collection; -import java.util.List; public class CharacterSeparatedStringToStringListTransformer extends TypeTransformer> { @Override public ArrayList transform(String argument) { - - ArrayList collection = Lists.newArrayList(); - for (String value : argument.split((String)additionalOptions[0])) { - collection.add(value); - } - return collection; + return Lists.newArrayList(argument.split((String)additionalOptions[0])); } } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringSetTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringSetTransformer.java index 11e889d6..2a152a95 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringSetTransformer.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringSetTransformer.java @@ -1,21 +1,13 @@ package com.tngtech.configbuilder.annotation.typetransformer; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; -import java.util.ArrayList; import java.util.HashSet; -import java.util.Set; public class CharacterSeparatedStringToStringSetTransformer extends TypeTransformer> { @Override public HashSet transform(String argument) { - - HashSet collection = Sets.newHashSet(); - for (String value : argument.split((String)additionalOptions[0])) { - collection.add(value); - } - return collection; + return Sets.newHashSet(argument.split((String)additionalOptions[0])); } } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToArrayListTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToArrayListTransformer.java index 4b504586..5ac0f52c 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToArrayListTransformer.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToArrayListTransformer.java @@ -4,21 +4,17 @@ import com.google.common.collect.Lists; import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; -import java.util.List; -public class CollectionToArrayListTransformer extends TypeTransformer { +public class CollectionToArrayListTransformer extends TypeTransformer { @Override public ArrayList transform(Collection argument) { ArrayList result = Lists.newArrayList(); - for(Object value : argument) { + for (Object value : argument) { result.add(fieldValueTransformer.performNecessaryTransformations(value, ((ParameterizedType) targetType).getActualTypeArguments()[0])); } return result; } - - } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToHashSetTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToHashSetTransformer.java index 6ebe295e..240a9aea 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToHashSetTransformer.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToHashSetTransformer.java @@ -6,12 +6,12 @@ import java.util.Collection; import java.util.HashSet; -public class CollectionToHashSetTransformer extends TypeTransformer { +public class CollectionToHashSetTransformer extends TypeTransformer { @Override public HashSet transform(Collection argument) { HashSet result = Sets.newHashSet(); - for(Object value : argument) { + for (Object value : argument) { result.add(fieldValueTransformer.performNecessaryTransformations(value, ((ParameterizedType) targetType).getActualTypeArguments()[0])); } return result; diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringCollectionToCommaSeparatedStringTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringCollectionToCommaSeparatedStringTransformer.java index fbf87c32..06b0b994 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringCollectionToCommaSeparatedStringTransformer.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringCollectionToCommaSeparatedStringTransformer.java @@ -5,10 +5,10 @@ import java.util.Collection; public class StringCollectionToCommaSeparatedStringTransformer extends TypeTransformer, String> { - + @Override public String transform(Collection argument) { - Joiner joiner = Joiner.on((String)additionalOptions[0]); + Joiner joiner = Joiner.on((String) additionalOptions[0]); return joiner.join(argument); } } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringOrPrimitiveToPrimitiveTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringOrPrimitiveToPrimitiveTransformer.java index ec760c1d..e2de48a9 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringOrPrimitiveToPrimitiveTransformer.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringOrPrimitiveToPrimitiveTransformer.java @@ -1,23 +1,19 @@ package com.tngtech.configbuilder.annotation.typetransformer; - import com.tngtech.configbuilder.exception.PrimitiveParsingException; -import com.tngtech.configbuilder.exception.TypeTransformerException; import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; -import java.lang.reflect.Type; -public class StringOrPrimitiveToPrimitiveTransformer extends TypeTransformer { +public class StringOrPrimitiveToPrimitiveTransformer extends TypeTransformer { @Override public Object transform(Object argument) { PropertyEditor editor = PropertyEditorManager.findEditor(genericsAndCastingHelper.castTypeToClass(targetType)); try { - editor.setAsText(String.valueOf(argument)); + editor.setAsText(String.valueOf(argument).trim()); return editor.getValue(); - } - catch(IllegalArgumentException e) { + } catch (IllegalArgumentException e) { throw new PrimitiveParsingException(errorMessageSetup.getErrorMessage(PrimitiveParsingException.class, String.valueOf(argument), targetType.toString())); } } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToColorTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToColorTransformer.java new file mode 100644 index 00000000..3610df67 --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToColorTransformer.java @@ -0,0 +1,11 @@ +package com.tngtech.configbuilder.annotation.typetransformer; + +import java.awt.*; + +public class StringToColorTransformer extends TypeTransformer { + + @Override + public Color transform(String colorTextValue) { + return Color.decode(colorTextValue); + } +} diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToEnumTypeTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToEnumTypeTransformer.java new file mode 100644 index 00000000..f3fd193b --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToEnumTypeTransformer.java @@ -0,0 +1,20 @@ +package com.tngtech.configbuilder.annotation.typetransformer; + +public class StringToEnumTypeTransformer> extends TypeTransformer { + + private final Class enumClass; + + public StringToEnumTypeTransformer(Class enumClass) { + this.enumClass = enumClass; + } + + @Override + public boolean isMatching(Class sourceClass, Class targetClass) { + return sourceClass.equals(String.class) && targetClass.equals(enumClass); + } + + @Override + public E transform(final String value) { + return E.valueOf(enumClass, value.trim().replace(' ', '_').toUpperCase()); + } +} diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToFileTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToFileTransformer.java new file mode 100644 index 00000000..e55bb1b1 --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToFileTransformer.java @@ -0,0 +1,10 @@ +package com.tngtech.configbuilder.annotation.typetransformer; + +import java.io.File; + +public class StringToFileTransformer extends TypeTransformer { + @Override + public File transform(String argument) { + return new File(argument); + } +} diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToLocaleTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToLocaleTransformer.java new file mode 100644 index 00000000..c31558b2 --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/StringToLocaleTransformer.java @@ -0,0 +1,10 @@ +package com.tngtech.configbuilder.annotation.typetransformer; + +import java.util.Locale; + +public class StringToLocaleTransformer extends TypeTransformer { + @Override + public Locale transform(String localeText) { + return Locale.forLanguageTag(localeText); + } +} diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformer.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformer.java index f9099ab0..0bfb1210 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformer.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformer.java @@ -5,13 +5,15 @@ import com.tngtech.configbuilder.util.ConfigBuilderFactory; import com.tngtech.configbuilder.util.FieldValueTransformer; -import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.ArrayList; +import java.lang.reflect.TypeVariable; +import java.util.HashMap; +import java.util.Map; /** * Implementations of this interface transform an object into a different type of object + * * @param the type of the parameter before the transformation * @param return type */ @@ -33,26 +35,57 @@ public void initialize(FieldValueTransformer fieldValueTransformer, ConfigBuilde } public boolean isMatching(Class sourceClass, Class targetClass) { - Class transformerSourceClass = getTransformerSourceClass(); - Class transformerTargetClass = getTransformerTargetClass(); - if(transformerSourceClass.isAssignableFrom(sourceClass) && targetClass.isAssignableFrom(transformerTargetClass)) { - return true; - } - return false; + return getTransformerSourceClass().isAssignableFrom(sourceClass) && + targetClass.isAssignableFrom(getTransformerTargetClass()); } protected Class getTransformerSourceClass() { - Type typeOfInterface = this.getClass().getGenericSuperclass(); - Type[] genericTypes = ((ParameterizedType) typeOfInterface).getActualTypeArguments(); + Type[] genericTypes = determineTypeArguments(); return genericsAndCastingHelper.castTypeToClass(genericTypes[0]); } protected Class getTransformerTargetClass() { - Type typeOfInterface = this.getClass().getGenericSuperclass(); - Type[] genericTypes = ((ParameterizedType) typeOfInterface).getActualTypeArguments(); + Type[] genericTypes = determineTypeArguments(); return genericsAndCastingHelper.castTypeToClass(genericTypes[1]); } + private Type[] determineTypeArguments() { + Class clazz = getClass(); + Type[] typeArguments = new Type[]{}; + + while (!clazz.equals(TypeTransformer.class)) { + TypeVariable[] typeParameters = clazz.getTypeParameters(); + + Map typeVariableMap = buildTypeNameMap(typeArguments, typeParameters); + + typeArguments = ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments(); + + replaceKnownTypes(typeArguments, typeVariableMap); + + clazz = clazz.getSuperclass(); + } + return typeArguments; + } + + private Map buildTypeNameMap(Type[] typeArguments, TypeVariable[] typeParameters) { + Map typeVariableMap = new HashMap<>(); + for (int i = 0; i < typeParameters.length; i++) { + typeVariableMap.put(typeParameters[i], typeArguments[i]); + } + return typeVariableMap; + } + + private void replaceKnownTypes(Type[] typeArguments, Map typeVariableMap) { + for (int i = 0; i < typeArguments.length; i++) { + if (typeArguments[i] instanceof TypeVariable) { + TypeVariable typeVariable = (TypeVariable) typeArguments[i]; + if (typeVariableMap.containsKey(typeVariable)) { + typeArguments[i] = typeVariableMap.get(typeVariable); + } + } + } + } + public void setTargetType(Type targetType) { this.targetType = targetType; } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformers.java b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformers.java index 8aef5a8e..3fd543dc 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformers.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformers.java @@ -12,5 +12,5 @@ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface TypeTransformers { - public Class[] value(); + Class[] value(); } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueDescriptor.java b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueDescriptor.java new file mode 100644 index 00000000..7182a93c --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueDescriptor.java @@ -0,0 +1,18 @@ +package com.tngtech.configbuilder.annotation.valueextractor; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This annotation is used to mark a method as description text supplier for command line options. + * The annotated method must be static and accept a single String parameter, which is the longOpt name of a command line option. + * There may be at most one such method per class.
+ * If a field is annotated with {@link com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue} but has no description, + * then this method is called to generate the description text.
+ * Usage: @CommandLineValueDescriptor + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +public @interface CommandLineValueDescriptor {} diff --git a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueProcessor.java index 2d9cc0c4..c5e03cf0 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueProcessor.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueProcessor.java @@ -1,23 +1,24 @@ package com.tngtech.configbuilder.annotation.valueextractor; - import com.tngtech.configbuilder.configuration.BuilderConfiguration; import com.tngtech.configbuilder.util.ConfigBuilderFactory; +import org.apache.commons.cli.CommandLine; import java.lang.annotation.Annotation; /** - * Processes CommandLineValue annotations, implements ValueExtractorProcessor + * Processes {@link CommandLineValue} annotations */ public class CommandLineValueProcessor implements ValueExtractorProcessor { public String getValue(Annotation annotation, ConfigBuilderFactory configBuilderFactory) { - BuilderConfiguration builderConfiguration = configBuilderFactory.getInstance(BuilderConfiguration.class); - - if (((CommandLineValue) annotation).hasArg()) { - return builderConfiguration.getCommandLine().getOptionValue(((CommandLineValue) annotation).shortOpt()); + CommandLine commandLine = configBuilderFactory.getInstance(BuilderConfiguration.class).getCommandLine(); + CommandLineValue commandLineValue = (CommandLineValue) annotation; + if (commandLineValue.hasArg()) { + return commandLine.getOptionValue(commandLineValue.shortOpt()); } else { - return String.valueOf(builderConfiguration.getCommandLine().hasOption(((CommandLineValue) annotation).shortOpt()) || builderConfiguration.getCommandLine().hasOption(((CommandLineValue) annotation).longOpt())); + boolean hasOption = commandLine.hasOption(commandLineValue.shortOpt()) || commandLine.hasOption(commandLineValue.longOpt()); + return String.valueOf(hasOption); } } } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/DefaultValueProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/DefaultValueProcessor.java index 3b5471a7..69fd9dc5 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/DefaultValueProcessor.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/DefaultValueProcessor.java @@ -5,7 +5,7 @@ import java.lang.annotation.Annotation; /** - * Processes DefaultValue annotations, implements ValueExtractorProcessor + * Processes {@link DefaultValue} annotations */ public class DefaultValueProcessor implements ValueExtractorProcessor { diff --git a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/EnvironmentVariableProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/EnvironmentVariableProcessor.java index e739128b..ebc2931d 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/EnvironmentVariableProcessor.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/EnvironmentVariableProcessor.java @@ -5,7 +5,7 @@ import java.lang.annotation.Annotation; /** - * Processes EnvironmentVariableValue annotations, implements ValueExtractorProcessor + * Processes {@link EnvironmentVariableValue} annotations */ public class EnvironmentVariableProcessor implements ValueExtractorProcessor { public String getValue(Annotation annotation, ConfigBuilderFactory configBuilderFactory) { diff --git a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/ImportedValueProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/ImportedValueProcessor.java index 9d2be835..5e887256 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/ImportedValueProcessor.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/ImportedValueProcessor.java @@ -15,10 +15,10 @@ public Object getValue(Annotation annotation, ConfigBuilderFactory configBuilder BuilderConfiguration builderConfiguration = configBuilderFactory.getInstance(BuilderConfiguration.class); Object importedConfiguration = builderConfiguration.getImportedConfiguration(); - if(importedConfiguration == null) { + if (importedConfiguration == null) { return null; } - + String fieldName = ((ImportedValue) annotation).value(); Object result; @@ -27,10 +27,14 @@ public Object getValue(Annotation annotation, ConfigBuilderFactory configBuilder field.setAccessible(true); result = field.get(importedConfiguration); } catch (NoSuchFieldException | IllegalAccessException e) { - ErrorMessageSetup errorMessageSetup = configBuilderFactory.getInstance(ErrorMessageSetup.class); - throw new ImportedConfigurationException(errorMessageSetup.getErrorMessage(ImportedConfigurationException.class, fieldName)); + throw createException(configBuilderFactory, fieldName); } return result; } + + private ImportedConfigurationException createException(ConfigBuilderFactory configBuilderFactory, String fieldName) { + ErrorMessageSetup errorMessageSetup = configBuilderFactory.getInstance(ErrorMessageSetup.class); + return new ImportedConfigurationException(errorMessageSetup.getErrorMessage(ImportedConfigurationException.class, fieldName)); + } } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/PropertyValueProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/PropertyValueProcessor.java index 553f9c1b..ce17ff86 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/PropertyValueProcessor.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/PropertyValueProcessor.java @@ -4,15 +4,25 @@ import com.tngtech.configbuilder.util.ConfigBuilderFactory; import java.lang.annotation.Annotation; +import java.util.Properties; /** - * Processes PropertyValue annotations, implements ValueExtractorProcessor + * Processes {@link PropertyValue} annotations */ public class PropertyValueProcessor implements ValueExtractorProcessor { public String getValue(Annotation annotation, ConfigBuilderFactory configBuilderFactory) { BuilderConfiguration builderConfiguration = configBuilderFactory.getInstance(BuilderConfiguration.class); - - return builderConfiguration.getProperties().getProperty(((PropertyValue) annotation).value()); + + final Properties properties = builderConfiguration.getProperties(); + final String propertyName = ((PropertyValue) annotation).value(); + + for (final String propertyNamePrefix : builderConfiguration.getPropertyNamePrefixes()) { + final String fullPropertyName = propertyNamePrefix + propertyName; + if (properties.containsKey(fullPropertyName)) { + return properties.getProperty(fullPropertyName); + } + } + return null; } } diff --git a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyProcessor.java index 55aae77d..4f16ff7b 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyProcessor.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyProcessor.java @@ -5,7 +5,7 @@ import java.lang.annotation.Annotation; /** - * Processes SystemPropertyValue annotations, implements ValueExtractorProcessor + * Processes {@link SystemPropertyValue} annotations */ public class SystemPropertyProcessor implements ValueExtractorProcessor { public String getValue(Annotation annotation, ConfigBuilderFactory configBuilderFactory) { diff --git a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyValue.java b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyValue.java index 7a7ba624..edde492a 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyValue.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyValue.java @@ -7,7 +7,7 @@ /** * This annotation is used to specify system properties.
- * Usage: @EnvironmentVariableValue("property.key") + * Usage: @SystemPropertyValue("property.key") */ @ValueExtractorAnnotation(SystemPropertyProcessor.class) @Target(ElementType.FIELD) diff --git a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/ValueExtractorProcessor.java b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/ValueExtractorProcessor.java index 2caf13e6..0cf084f1 100644 --- a/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/ValueExtractorProcessor.java +++ b/src/main/java/com/tngtech/configbuilder/annotation/valueextractor/ValueExtractorProcessor.java @@ -9,5 +9,5 @@ * This interface is implemented by annotation processors that get String values from annotations. */ public interface ValueExtractorProcessor { - public Object getValue(Annotation annotation, ConfigBuilderFactory configBuilderFactory); + Object getValue(Annotation annotation, ConfigBuilderFactory configBuilderFactory); } diff --git a/src/main/java/com/tngtech/configbuilder/configuration/BuilderConfiguration.java b/src/main/java/com/tngtech/configbuilder/configuration/BuilderConfiguration.java index 97a63d8a..3f61ebb7 100644 --- a/src/main/java/com/tngtech/configbuilder/configuration/BuilderConfiguration.java +++ b/src/main/java/com/tngtech/configbuilder/configuration/BuilderConfiguration.java @@ -11,16 +11,11 @@ */ public class BuilderConfiguration { - private Properties properties; - private CommandLine commandLine; - private Object importedConfiguration; + private Properties properties = new Properties(); + private CommandLine commandLine = null; + private Object importedConfiguration = null; private Class[] annotationOrder = new Class[]{CommandLineValue.class, PropertyValue.class, EnvironmentVariableValue.class, SystemPropertyValue.class, ImportedValue.class, DefaultValue.class}; - - public BuilderConfiguration() { - properties = new Properties(); - commandLine = null; - importedConfiguration = null; - } + private String[] propertyNamePrefixes = {""}; public CommandLine getCommandLine() { return commandLine; @@ -53,4 +48,12 @@ public void setAnnotationOrder(Class[] annotationOrder) { public Class[] getAnnotationOrder() { return annotationOrder; } + + public void setPropertyNamePrefixes(String[] propertyNamePrefixes) { + this.propertyNamePrefixes = propertyNamePrefixes; + } + + public String[] getPropertyNamePrefixes() { + return propertyNamePrefixes; + } } diff --git a/src/main/java/com/tngtech/configbuilder/configuration/ErrorMessageSetup.java b/src/main/java/com/tngtech/configbuilder/configuration/ErrorMessageSetup.java index 204e985a..a1bc94fc 100644 --- a/src/main/java/com/tngtech/configbuilder/configuration/ErrorMessageSetup.java +++ b/src/main/java/com/tngtech/configbuilder/configuration/ErrorMessageSetup.java @@ -43,7 +43,7 @@ public String getErrorMessage(Throwable e, String... variables) { return message == null ? String.format(errorMessages.getProperty("standardMessage"), e.getClass().getName()) : String.format(message, variables); } - public String getErrorMessage(Class exceptionClass, String... variables) { + public String getErrorMessage(Class exceptionClass, String... variables) { String message = errorMessages.getProperty(exceptionClass.getName()); return message == null ? String.format(errorMessages.getProperty("standardMessage"), exceptionClass.getName()) : String.format(message, variables); } diff --git a/src/main/java/com/tngtech/configbuilder/exception/InvalidDescriptionMethodException.java b/src/main/java/com/tngtech/configbuilder/exception/InvalidDescriptionMethodException.java new file mode 100644 index 00000000..bd4f4f92 --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/exception/InvalidDescriptionMethodException.java @@ -0,0 +1,3 @@ +package com.tngtech.configbuilder.exception; + +public class InvalidDescriptionMethodException extends RuntimeException {} diff --git a/src/main/java/com/tngtech/configbuilder/exception/ValidatorException.java b/src/main/java/com/tngtech/configbuilder/exception/ValidatorException.java index f27229af..b54abbe8 100644 --- a/src/main/java/com/tngtech/configbuilder/exception/ValidatorException.java +++ b/src/main/java/com/tngtech/configbuilder/exception/ValidatorException.java @@ -1,7 +1,5 @@ package com.tngtech.configbuilder.exception; -import com.google.common.base.Function; -import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import javax.validation.ConstraintViolation; @@ -9,16 +7,11 @@ public class ValidatorException extends RuntimeException { - Set constraintViolations; + Set> constraintViolations; public ValidatorException(String message, Set> constraintViolations) { super(message); - this.constraintViolations = Sets.newHashSet(Iterables.transform(constraintViolations, new Function, ConstraintViolation>() { - @Override - public ConstraintViolation apply(ConstraintViolation constraintViolation) { - return constraintViolation; - } - })); + this.constraintViolations = Sets.newHashSet(constraintViolations); } public ValidatorException(String message, Throwable e) { diff --git a/src/main/java/com/tngtech/configbuilder/util/AnnotationHelper.java b/src/main/java/com/tngtech/configbuilder/util/AnnotationHelper.java index 6ed68c56..6079cf17 100644 --- a/src/main/java/com/tngtech/configbuilder/util/AnnotationHelper.java +++ b/src/main/java/com/tngtech/configbuilder/util/AnnotationHelper.java @@ -33,11 +33,11 @@ public List getAnnotationsInOrder(Field field, Class getFieldsAnnotatedWith(Class clazz, Class annotationClass) { + public Set getFieldsAnnotatedWith(Class clazz, Class annotationClass) { return getAllFields(clazz, withAnnotation(annotationClass)); } - public Set getMethodsAnnotatedWith(Class clazz, Class annotationClass) { + public Set getMethodsAnnotatedWith(Class clazz, Class annotationClass) { return getAllMethods(clazz, withAnnotation(annotationClass)); } diff --git a/src/main/java/com/tngtech/configbuilder/util/CommandLineHelper.java b/src/main/java/com/tngtech/configbuilder/util/CommandLineHelper.java index 3da0e556..3716a737 100644 --- a/src/main/java/com/tngtech/configbuilder/util/CommandLineHelper.java +++ b/src/main/java/com/tngtech/configbuilder/util/CommandLineHelper.java @@ -1,16 +1,23 @@ package com.tngtech.configbuilder.util; import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; +import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValueDescriptor; import com.tngtech.configbuilder.configuration.ErrorMessageSetup; import com.tngtech.configbuilder.exception.ConfigBuilderException; +import com.tngtech.configbuilder.exception.InvalidDescriptionMethodException; import org.apache.commons.cli.*; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Set; + +import static com.google.common.collect.Iterables.getOnlyElement; public class CommandLineHelper { - private final static Logger log = Logger.getLogger(CommandLineHelper.class); + private final static Logger log = LoggerFactory.getLogger(CommandLineHelper.class); private final ConfigBuilderFactory configBuilderFactory; private final AnnotationHelper annotationHelper; @@ -22,36 +29,57 @@ public CommandLineHelper(ConfigBuilderFactory configBuilderFactory) { this.errorMessageSetup = configBuilderFactory.getInstance(ErrorMessageSetup.class); } - public CommandLine getCommandLine(Class configClass, String[] args) { + public CommandLine getCommandLine(Class configClass, String[] args) { log.info("getting command line options from fields and parsing command line arguments"); Options options = getOptions(configClass); return parseCommandLine(args, options); } - public Options getOptions(Class configClass) { + public Options getOptions(Class configClass) { Options options = configBuilderFactory.createInstance(Options.class); for (Field field : annotationHelper.getFieldsAnnotatedWith(configClass, CommandLineValue.class)) { - options.addOption(getOption(field)); + if (!field.isSynthetic()) { + options.addOption(getOption(field, configClass)); + } } return options; } - @SuppressWarnings("AccessStaticViaInstance") - private Option getOption(Field field) { + private Option getOption(Field field, Class configClass) { CommandLineValue commandLineValue = field.getAnnotation(CommandLineValue.class); - log.debug(String.format("adding command line option %s for field %s", commandLineValue.shortOpt(), field.getName())); - return OptionBuilder.withLongOpt(commandLineValue.longOpt()) + log.debug("adding command line option {} for field {}", commandLineValue.shortOpt(), field.getName()); + return Option.builder(commandLineValue.shortOpt()) + .longOpt(commandLineValue.longOpt()) .hasArg() - .isRequired(commandLineValue.required()) - .withDescription(commandLineValue.description()) + .required(commandLineValue.required()) + .desc(extractDescriptionString(commandLineValue, configClass)) .hasArg(commandLineValue.hasArg()) - .create(commandLineValue.shortOpt()); + .build(); + } + + private String extractDescriptionString(CommandLineValue commandLineValue, Class configClass) { + if (!commandLineValue.description().isEmpty()) { + return commandLineValue.description(); + } + + Set descriptorMethods = annotationHelper.getMethodsAnnotatedWith(configClass, CommandLineValueDescriptor.class); + if (descriptorMethods.isEmpty()) { + return ""; + } + + try { + Method descriptorMethod = getOnlyElement(descriptorMethods); + descriptorMethod.setAccessible(true); + return descriptorMethod.invoke(null, commandLineValue.longOpt()).toString(); + } catch (Exception e) { + throw new ConfigBuilderException(errorMessageSetup.getErrorMessage(InvalidDescriptionMethodException.class), e); + } } private CommandLine parseCommandLine(String[] args, Options options) { CommandLine commandLine; try { - commandLine = configBuilderFactory.createInstance(GnuParser.class).parse(options, args); + commandLine = configBuilderFactory.createInstance(DefaultParser.class).parse(options, args); } catch (ParseException e) { throw new ConfigBuilderException(errorMessageSetup.getErrorMessage(e.getClass().getSuperclass()), e); } diff --git a/src/main/java/com/tngtech/configbuilder/util/ConfigBuilderFactory.java b/src/main/java/com/tngtech/configbuilder/util/ConfigBuilderFactory.java index 9a382584..a481d2b5 100644 --- a/src/main/java/com/tngtech/configbuilder/util/ConfigBuilderFactory.java +++ b/src/main/java/com/tngtech/configbuilder/util/ConfigBuilderFactory.java @@ -18,7 +18,7 @@ public class ConfigBuilderFactory { - private Map singletonMap = Maps.newHashMap(); + private final Map, Object> singletonMap = Maps.newHashMap(); public void initialize() { @@ -86,7 +86,7 @@ public K createInstance(Class clazz) { //TODO: exception message private K createInstanceOfInnerClass(Class clazz) { - Class superClass = clazz.getDeclaringClass(); + Class superClass = clazz.getDeclaringClass(); try { Object superInstance = superClass.newInstance(); Constructor constructor = clazz.getConstructor(superClass); diff --git a/src/main/java/com/tngtech/configbuilder/util/ConfigValidator.java b/src/main/java/com/tngtech/configbuilder/util/ConfigValidator.java index 1ffb855e..6ac9fcb5 100644 --- a/src/main/java/com/tngtech/configbuilder/util/ConfigValidator.java +++ b/src/main/java/com/tngtech/configbuilder/util/ConfigValidator.java @@ -4,16 +4,18 @@ import com.tngtech.configbuilder.annotation.validation.Validation; import com.tngtech.configbuilder.configuration.ErrorMessageSetup; import com.tngtech.configbuilder.exception.ValidatorException; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.validation.ConstraintViolation; +import javax.validation.Validator; import javax.validation.ValidatorFactory; import java.lang.reflect.Method; import java.util.Set; public class ConfigValidator { - private final static Logger log = Logger.getLogger(ConfigValidator.class); + private final static Logger log = LoggerFactory.getLogger(ConfigValidator.class); private final ConfigBuilderFactory configBuilderFactory; private final ErrorMessageSetup errorMessageSetup; @@ -26,7 +28,7 @@ public ConfigValidator(ConfigBuilderFactory miscFactory) { } public void validate(T instanceOfConfigClass) { - log.info(String.format("validating instance of %s", instanceOfConfigClass.getClass())); + log.debug("validating instance of {}", instanceOfConfigClass.getClass()); callValidationMethods(instanceOfConfigClass); callJSRValidation(instanceOfConfigClass); } @@ -44,11 +46,11 @@ private void callValidationMethods(T instanceOfConfigClass) { private void callJSRValidation(T instanceOfConfigClass) { ValidatorFactory factory = configBuilderFactory.getInstance(ValidatorFactory.class); - javax.validation.Validator validator = factory.getValidator(); + Validator validator = factory.getValidator(); Set> constraintViolations = validator.validate(instanceOfConfigClass); if (!constraintViolations.isEmpty()) { StringBuilder stringBuilder = new StringBuilder(errorMessageSetup.getErrorMessage(ValidatorException.class) + "\n"); - for (ConstraintViolation constraintViolation : constraintViolations) { + for (ConstraintViolation constraintViolation : constraintViolations) { stringBuilder.append(constraintViolation.getMessage()); } throw new ValidatorException(stringBuilder.toString(), constraintViolations); diff --git a/src/main/java/com/tngtech/configbuilder/util/ConstructionHelper.java b/src/main/java/com/tngtech/configbuilder/util/ConstructionHelper.java index 62becb06..19ab2637 100644 --- a/src/main/java/com/tngtech/configbuilder/util/ConstructionHelper.java +++ b/src/main/java/com/tngtech/configbuilder/util/ConstructionHelper.java @@ -3,16 +3,17 @@ import com.tngtech.configbuilder.configuration.ErrorMessageSetup; import com.tngtech.configbuilder.exception.ConfigBuilderException; import com.tngtech.configbuilder.exception.NoConstructorFoundException; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class ConstructionHelper { - private final static Logger log = Logger.getLogger(ConstructionHelper.class); + private final static Logger log = LoggerFactory.getLogger(ConstructionHelper.class); - private ErrorMessageSetup errorMessageSetup; + private final ErrorMessageSetup errorMessageSetup; public ConstructionHelper(ConfigBuilderFactory configBuilderFactory) { this.errorMessageSetup = configBuilderFactory.getInstance(ErrorMessageSetup.class); @@ -21,17 +22,21 @@ public ConstructionHelper(ConfigBuilderFactory configBuilderFactory) { public T getInstance(Class configClass, Object... objects) { try { Constructor tConstructor = findSuitableConstructor(configClass, objects); - log.info(String.format("found constructor - instantiating %s", configClass.getName())); + log.debug("found constructor - instantiating {}", configClass.getName()); tConstructor.setAccessible(true); return tConstructor.newInstance(objects); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { - throw new ConfigBuilderException(errorMessageSetup.getErrorMessage(e), e); + } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { + throw createConfigBuilderException(e); } } + private ConfigBuilderException createConfigBuilderException(Exception e) { + return new ConfigBuilderException(errorMessageSetup.getErrorMessage(e), e); + } + @SuppressWarnings("unchecked") private Constructor findSuitableConstructor(Class configClass, Object... objects) { - log.debug(String.format("trying to find a constructor for %s matching the arguments of build()", configClass.getName())); + log.debug("trying to find a constructor for {} matching the arguments of build()", configClass.getName()); Constructor[] constructors = configClass.getDeclaredConstructors(); for (Constructor constructor : constructors) { if (constructorIsMatching(constructor, objects)) { diff --git a/src/main/java/com/tngtech/configbuilder/util/EnumTypeExtractor.java b/src/main/java/com/tngtech/configbuilder/util/EnumTypeExtractor.java new file mode 100644 index 00000000..ce4dbdda --- /dev/null +++ b/src/main/java/com/tngtech/configbuilder/util/EnumTypeExtractor.java @@ -0,0 +1,23 @@ +package com.tngtech.configbuilder.util; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.stream.Stream; + +import static java.util.Arrays.stream; + +public class EnumTypeExtractor { + + Stream>> getEnumTypesRelevantFor(Type type) { + if (type instanceof Class && ((Class) type).isEnum()) { + return Stream.of((Class>) type); + } + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + return stream(parameterizedType.getActualTypeArguments()) + .flatMap(this::getEnumTypesRelevantFor) + .distinct(); + } + return Stream.empty(); + } +} diff --git a/src/main/java/com/tngtech/configbuilder/util/FieldSetter.java b/src/main/java/com/tngtech/configbuilder/util/FieldSetter.java index 2ec2d3bb..e8799c16 100644 --- a/src/main/java/com/tngtech/configbuilder/util/FieldSetter.java +++ b/src/main/java/com/tngtech/configbuilder/util/FieldSetter.java @@ -1,16 +1,20 @@ package com.tngtech.configbuilder.util; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; import com.tngtech.configbuilder.annotation.valueextractor.ValueExtractorAnnotation; import com.tngtech.configbuilder.configuration.BuilderConfiguration; import com.tngtech.configbuilder.configuration.ErrorMessageSetup; import com.tngtech.configbuilder.exception.ConfigBuilderException; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Field; +import java.util.List; public class FieldSetter { - private final static Logger log = Logger.getLogger(FieldSetter.class); + private final static Logger log = LoggerFactory.getLogger(FieldSetter.class); private final FieldValueTransformer fieldValueTransformer; private final FieldValueExtractor fieldValueExtractor; @@ -26,26 +30,42 @@ public FieldSetter(ConfigBuilderFactory configBuilderFactory) { public void setFields(T instanceOfConfigClass, BuilderConfiguration builderConfiguration) { - for (Field field : instanceOfConfigClass.getClass().getDeclaredFields()) { + for (Field field : getInheritedPrivateFields(instanceOfConfigClass.getClass())) { + if (field.isSynthetic()) { + continue; + } + if (annotationHelper.fieldHasAnnotationAnnotatedWith(field, ValueExtractorAnnotation.class)) { Object value = fieldValueExtractor.extractValue(field, builderConfiguration); value = fieldValueTransformer.transformFieldValue(field, value); setField(instanceOfConfigClass, field, value); } else { - log.debug(String.format("field %s is not annotated with any ValueExtractorAnnotation: skipping field", field.getName())); + log.debug("field {} is not annotated with any ValueExtractorAnnotation: skipping field", field.getName()); } } } + public static List getInheritedPrivateFields(Class type) { + final ImmutableList.Builder listBuilder = ImmutableList.builder(); + + Class currentType = type; + while (currentType != null && currentType != Object.class) { + listBuilder.addAll(Lists.newArrayList(currentType.getDeclaredFields())); + currentType = currentType.getSuperclass(); + } + + return listBuilder.build(); + } + private void setField(T instanceOfConfigClass, Field field, Object value) { try { field.setAccessible(true); if(value == null && field.getType().isPrimitive()) { - log.warn(String.format("no value found for field %s of primitive type %s: field will be initialized to default", field.getName(), field.getType().getName())); + log.warn("no value found for field {} of primitive type {}: field will be initialized to default", field.getName(), field.getType().getName()); } else { field.set(instanceOfConfigClass, value); - log.info(String.format("set field %s of type %s to a value of type %s", field.getName(), field.getType().getName(), value == null ? "null" : value.getClass().getName())); + log.debug("set field {} of type {} to a value of type {}", field.getName(), field.getType().getName(), value == null ? "null" : value.getClass().getName()); } } catch (Exception e) { throw new ConfigBuilderException(errorMessageSetup.getErrorMessage(e, field.getName(), field.getType().getName(), value == null ? "null" : value.toString()), e); diff --git a/src/main/java/com/tngtech/configbuilder/util/FieldValueExtractor.java b/src/main/java/com/tngtech/configbuilder/util/FieldValueExtractor.java index 218c0403..33223306 100644 --- a/src/main/java/com/tngtech/configbuilder/util/FieldValueExtractor.java +++ b/src/main/java/com/tngtech/configbuilder/util/FieldValueExtractor.java @@ -1,17 +1,19 @@ package com.tngtech.configbuilder.util; +import com.tngtech.configbuilder.annotation.configuration.DoNotLogValue; import com.tngtech.configbuilder.annotation.configuration.LoadingOrder; -import com.tngtech.configbuilder.annotation.valueextractor.ValueExtractorProcessor; import com.tngtech.configbuilder.annotation.valueextractor.ValueExtractorAnnotation; +import com.tngtech.configbuilder.annotation.valueextractor.ValueExtractorProcessor; import com.tngtech.configbuilder.configuration.BuilderConfiguration; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.annotation.Annotation; import java.lang.reflect.Field; public class FieldValueExtractor { - private final static Logger log = Logger.getLogger(FieldValueExtractor.class); + private final static Logger log = LoggerFactory.getLogger(FieldValueExtractor.class); private final AnnotationHelper annotationHelper; private final ConfigBuilderFactory configBuilderFactory; @@ -23,15 +25,18 @@ public FieldValueExtractor(ConfigBuilderFactory configBuilderFactory) { public Object extractValue(Field field, BuilderConfiguration builderConfiguration) { Object value = null; + boolean doNotLogValue = field.isAnnotationPresent(DoNotLogValue.class); Class[] annotationOrderOfField = field.isAnnotationPresent(LoadingOrder.class) ? field.getAnnotation(LoadingOrder.class).value() : builderConfiguration.getAnnotationOrder(); - Class processor; - for (Annotation annotation : annotationHelper.getAnnotationsInOrder(field, annotationOrderOfField)) { - log.debug(String.format("trying to find a value for field %s with %s annotation", field.getName(), annotation.annotationType())); - processor = annotation.annotationType().getAnnotation(ValueExtractorAnnotation.class).value(); + log.debug("trying to find a value for field {} with {} annotation", field.getName(), annotation.annotationType()); + Class processor = annotation.annotationType().getAnnotation(ValueExtractorAnnotation.class).value(); value = configBuilderFactory.getInstance(processor).getValue(annotation, configBuilderFactory); if (value != null) { - log.debug(String.format("found value \"%s\" for field %s from %s annotation", value, field.getName(), annotation.annotationType())); + if (doNotLogValue) { + log.debug("found value for field {} from {} annotation", field.getName(), annotation.annotationType()); + } else { + log.debug("found value \"{}\" for field {} from {} annotation", value, field.getName(), annotation.annotationType()); + } break; } } diff --git a/src/main/java/com/tngtech/configbuilder/util/FieldValueTransformer.java b/src/main/java/com/tngtech/configbuilder/util/FieldValueTransformer.java index ac2a9f8d..c8c612cb 100644 --- a/src/main/java/com/tngtech/configbuilder/util/FieldValueTransformer.java +++ b/src/main/java/com/tngtech/configbuilder/util/FieldValueTransformer.java @@ -5,7 +5,8 @@ import com.tngtech.configbuilder.annotation.typetransformer.*; import com.tngtech.configbuilder.configuration.ErrorMessageSetup; import com.tngtech.configbuilder.exception.TypeTransformerException; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.lang.reflect.Field; import java.lang.reflect.Type; @@ -14,15 +15,16 @@ //TODO: Content transformers (transform even if types already match, allow null as argument) public class FieldValueTransformer { - private final static Logger log = Logger.getLogger(FieldValueTransformer.class); + private final static Logger log = LoggerFactory.getLogger(FieldValueTransformer.class); private final ConfigBuilderFactory configBuilderFactory; private final ErrorMessageSetup errorMessageSetup; private final GenericsAndCastingHelper genericsAndCastingHelper; + private final EnumTypeExtractor enumTypeExtractor; private Object[] additionalOptions; //Order is important: Prefer List over Set if both apply! - private final ArrayList> defaultTransformers = Lists.>newArrayList( + private final ArrayList> defaultTransformers = Lists.newArrayList( StringOrPrimitiveToPrimitiveTransformer.class, CharacterSeparatedStringToStringListTransformer.class, CharacterSeparatedStringToStringSetTransformer.class, @@ -37,6 +39,7 @@ public FieldValueTransformer(ConfigBuilderFactory configBuilderFactory) { this.configBuilderFactory = configBuilderFactory; this.errorMessageSetup = configBuilderFactory.getInstance(ErrorMessageSetup.class); this.genericsAndCastingHelper = configBuilderFactory.getInstance(GenericsAndCastingHelper.class); + this.enumTypeExtractor = configBuilderFactory.getInstance(EnumTypeExtractor.class); } public Object transformFieldValue(Field field, Object sourceValue) { @@ -50,6 +53,10 @@ private void initialize(Field field) { for(Class transformerClass : getAllTransformers(field)) { availableTransformers.add(configBuilderFactory.getInstance(transformerClass)); } + enumTypeExtractor.getEnumTypesRelevantFor(field.getGenericType()) + .map(enumClass -> new StringToEnumTypeTransformer(enumClass)) + .forEach(availableTransformers::add); + additionalOptions = field.isAnnotationPresent(Separator.class)? new Object[]{field.getAnnotation(Separator.class).value()} : new Object[]{","}; } @@ -75,7 +82,7 @@ public Object performNecessaryTransformations(Object sourceValue, Type targetTyp Class sourceClass = genericsAndCastingHelper.getWrapperClassIfPrimitive(sourceValue.getClass()); Class targetClass = genericsAndCastingHelper.castTypeToClass(targetType); - log.info(String.format("Searching for a transformer from %s to %s", sourceClass.getSimpleName(), targetClass.getSimpleName())); + log.debug("Searching for a transformer from {} to {}", sourceClass.getSimpleName(), targetClass.getSimpleName()); TypeTransformer transformer = findApplicableTransformer(sourceClass, targetType); sourceValue = transformer.transform(sourceValue); diff --git a/src/main/java/com/tngtech/configbuilder/util/GenericsAndCastingHelper.java b/src/main/java/com/tngtech/configbuilder/util/GenericsAndCastingHelper.java index b89125b3..e426d63d 100644 --- a/src/main/java/com/tngtech/configbuilder/util/GenericsAndCastingHelper.java +++ b/src/main/java/com/tngtech/configbuilder/util/GenericsAndCastingHelper.java @@ -1,28 +1,26 @@ package com.tngtech.configbuilder.util; +import com.google.common.collect.ImmutableMap; + import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; -import java.util.HashMap; import java.util.Map; public class GenericsAndCastingHelper { - private final Map, Class> primitiveToWrapperMapping; - - public GenericsAndCastingHelper() { - primitiveToWrapperMapping = new HashMap<>(); - primitiveToWrapperMapping.put(boolean.class, Boolean.class); - primitiveToWrapperMapping.put(byte.class, Byte.class); - primitiveToWrapperMapping.put(short.class, Short.class); - primitiveToWrapperMapping.put(char.class, Character.class); - primitiveToWrapperMapping.put(int.class, Integer.class); - primitiveToWrapperMapping.put(long.class, Long.class); - primitiveToWrapperMapping.put(float.class, Float.class); - primitiveToWrapperMapping.put(double.class, Double.class); - } + private final Map, Class> primitiveToWrapperMapping = ImmutableMap., Class>builder() + .put(boolean.class, Boolean.class) + .put(byte.class, Byte.class) + .put(short.class, Short.class) + .put(char.class, Character.class) + .put(int.class, Integer.class) + .put(long.class, Long.class) + .put(float.class, Float.class) + .put(double.class, Double.class) + .build(); - public Class getWrapperClassIfPrimitive(Class clazz) { + public Class getWrapperClassIfPrimitive(Class clazz) { return primitiveToWrapperMapping.get(clazz) == null? clazz : primitiveToWrapperMapping.get(clazz); } @@ -45,7 +43,7 @@ public boolean typesMatch(Object sourceValue, Type targetType) { else if(Collection.class.isAssignableFrom((Class)((ParameterizedType)targetType).getRawType())) { if(Collection.class.isAssignableFrom(sourceClass)) { Class typeArgument = (Class)((ParameterizedType) targetType).getActualTypeArguments()[0]; - for(Object item : (Collection)sourceValue) { + for(Object item : (Collection)sourceValue) { if(!typeArgument.isAssignableFrom(item.getClass())) { return false; } @@ -59,7 +57,7 @@ else if(Collection.class.isAssignableFrom((Class)((ParameterizedType)targetTy return (castTypeToClass(targetType)).isAssignableFrom(sourceClass); } - public boolean isPrimitiveOrWrapper(Class targetClass) { + public boolean isPrimitiveOrWrapper(Class targetClass) { return primitiveToWrapperMapping.containsKey(targetClass) || primitiveToWrapperMapping.containsValue(targetClass); } } diff --git a/src/main/java/com/tngtech/configbuilder/util/PropertyLoaderConfigurator.java b/src/main/java/com/tngtech/configbuilder/util/PropertyLoaderConfigurator.java index 0e192a7d..4693e35a 100644 --- a/src/main/java/com/tngtech/configbuilder/util/PropertyLoaderConfigurator.java +++ b/src/main/java/com/tngtech/configbuilder/util/PropertyLoaderConfigurator.java @@ -8,8 +8,8 @@ import java.lang.annotation.Annotation; public class PropertyLoaderConfigurator { - private AnnotationHelper annotationHelper; - private ConfigBuilderFactory configBuilderFactory; + private final AnnotationHelper annotationHelper; + private final ConfigBuilderFactory configBuilderFactory; public PropertyLoaderConfigurator(ConfigBuilderFactory configBuilderFactory) { this.annotationHelper = configBuilderFactory.getInstance(AnnotationHelper.class); @@ -17,7 +17,6 @@ public PropertyLoaderConfigurator(ConfigBuilderFactory configBuilderFactory) { } public PropertyLoader configurePropertyLoader(Class configClass) { - PropertyLoader propertyLoader = configBuilderFactory.createInstance(PropertyLoader.class).withDefaultConfig(); for (Annotation annotation : annotationHelper.getAnnotationsAnnotatedWith(configClass.getDeclaredAnnotations(), PropertyLoaderConfigurationAnnotation.class)) { Class processorClass = annotation.annotationType().getAnnotation(PropertyLoaderConfigurationAnnotation.class).value(); diff --git a/src/main/resources/errors.properties b/src/main/resources/errors.properties index 2cbc8181..222bfc4e 100644 --- a/src/main/resources/errors.properties +++ b/src/main/resources/errors.properties @@ -11,4 +11,5 @@ com.tngtech.configbuilder.exception.TypeTransformerException = couldn't find a t com.tngtech.configbuilder.exception.PrimitiveParsingException = unable to parse "%s" to %s com.tngtech.configbuilder.exception.ImportedConfigurationException = couldn't find a field with the name %s com.tngtech.configbuilder.exception.FactoryInstantiationException = could not create an instance of %s +com.tngtech.configbuilder.exception.InvalidDescriptionMethodException = invalid or multiple use of the @CommandLineValueDescriptor annotation (the annotated method must be static and accept a single String parameter) standardMessage = %s was thrown \ No newline at end of file diff --git a/src/main/resources/errors_de.properties b/src/main/resources/errors_de.properties index 30fb0255..ce0a6d5f 100644 --- a/src/main/resources/errors_de.properties +++ b/src/main/resources/errors_de.properties @@ -11,4 +11,5 @@ com.tngtech.configbuilder.exception.TypeTransformerException = Konnte keinen Tra com.tngtech.configbuilder.exception.PrimitiveParsingException = Kann "%s" nicht zu %s verarbeiten! com.tngtech.configbuilder.exception.ImportedConfigurationException = Konnte kein Feld mit dem Namen %s finden. com.tngtech.configbuilder.exception.FactoryInstantiationException = Konnte keine Instanz von %s erzeugen. +com.tngtech.configbuilder.exception.InvalidDescriptionMethodException = ungültige oder mehrfache Verwendung der @CommandLineValueDescriptor-Annotation (die annotierte Methode muss statisch sein und einen einzelnen String-Parameter haben) standardMessage = Es gab eine Exception vom Typ %s \ No newline at end of file diff --git a/src/main/resources/errors_en.properties b/src/main/resources/errors_en.properties index 2cbc8181..222bfc4e 100644 --- a/src/main/resources/errors_en.properties +++ b/src/main/resources/errors_en.properties @@ -11,4 +11,5 @@ com.tngtech.configbuilder.exception.TypeTransformerException = couldn't find a t com.tngtech.configbuilder.exception.PrimitiveParsingException = unable to parse "%s" to %s com.tngtech.configbuilder.exception.ImportedConfigurationException = couldn't find a field with the name %s com.tngtech.configbuilder.exception.FactoryInstantiationException = could not create an instance of %s +com.tngtech.configbuilder.exception.InvalidDescriptionMethodException = invalid or multiple use of the @CommandLineValueDescriptor annotation (the annotated method must be static and accept a single String parameter) standardMessage = %s was thrown \ No newline at end of file diff --git a/src/test/java/com/tngtech/configbuilder/ConfigBuilderComponentTest.java b/src/test/java/com/tngtech/configbuilder/ConfigBuilderComponentTest.java deleted file mode 100644 index 103e5a67..00000000 --- a/src/test/java/com/tngtech/configbuilder/ConfigBuilderComponentTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.tngtech.configbuilder; - -import com.tngtech.configbuilder.annotation.valueextractor.*; -import com.tngtech.configbuilder.configuration.BuilderConfiguration; -import com.tngtech.configbuilder.testclasses.TestConfig; -import com.tngtech.configbuilder.util.ConfigBuilderFactory; -import com.tngtech.propertyloader.PropertyLoader; -import org.apache.commons.cli.*; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -import java.util.Properties; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class ConfigBuilderComponentTest { - - private Properties properties; - private Properties errors; - - @Mock - private BuilderConfiguration builderConfiguration; - - @Mock - private ConfigBuilderFactory configBuilderFactory; - - @Mock - private PropertyLoader propertyLoader; - - @Before - public void setUp() { - properties = new Properties(); - errors = new Properties(); - properties.put("thisisaproperty", ""); - errors.put("thisisanerrormessage", ""); - } - - @Test - public void testThatCommandLineValueHandlerLoadsStringFromAnnotation() { - try { - CommandLineValue commandLineValue = TestConfig.class.getDeclaredField("surName").getAnnotation(CommandLineValue.class); - - String[] args = new String[]{"-u", "Mueller"}; - Options options = new Options(); - options.addOption("u", true, "testString"); - CommandLineParser parser = new GnuParser(); - try { - CommandLine commandLineArgs = parser.parse(options, args); - CommandLineValueProcessor commandLineValueProcessor = new CommandLineValueProcessor(); - when(builderConfiguration.getCommandLine()).thenReturn(commandLineArgs); - String result = commandLineValueProcessor.getValue(commandLineValue, configBuilderFactory); - assertEquals("true", result); - - } catch (ParseException e) { - } - - - } catch (NoSuchFieldException e) { - } - } - - @Test - public void testThatPropertyValueHandlerLoadsStringFromAnnotation() { - try { - PropertyValue propertyValue = TestConfig.class.getDeclaredField("helloWorld").getAnnotation(PropertyValue.class); - - Properties properties = new Properties(); - properties.put("a", "HelloWorld"); - - PropertyValueProcessor propertyValueProcessor = new PropertyValueProcessor(); - - when(configBuilderFactory.getInstance(BuilderConfiguration.class)).thenReturn(builderConfiguration); - when(builderConfiguration.getProperties()).thenReturn(properties); - - String result = propertyValueProcessor.getValue(propertyValue, configBuilderFactory); - assertEquals("HelloWorld", result); - } catch (NoSuchFieldException e) { - } - } - - @Test - public void testThatDefaultValueHandlerLoadsStringFromAnnotation() { - try { - DefaultValue defaultValue = TestConfig.class.getDeclaredField("userName").getAnnotation(DefaultValue.class); - DefaultValueProcessor defaultValueProcessor = new DefaultValueProcessor(); - String result = defaultValueProcessor.getValue(defaultValue, configBuilderFactory); - assertEquals("3", result); - } catch (NoSuchFieldException e) { - } - } -} diff --git a/src/test/java/com/tngtech/configbuilder/ConfigBuilderExceptionTest.java b/src/test/java/com/tngtech/configbuilder/ConfigBuilderExceptionTest.java index 356b63ad..26dc4d1d 100644 --- a/src/test/java/com/tngtech/configbuilder/ConfigBuilderExceptionTest.java +++ b/src/test/java/com/tngtech/configbuilder/ConfigBuilderExceptionTest.java @@ -3,53 +3,31 @@ import com.tngtech.configbuilder.exception.ConfigBuilderException; import com.tngtech.configbuilder.exception.NoConstructorFoundException; import com.tngtech.configbuilder.exception.PrimitiveParsingException; -import com.tngtech.configbuilder.testclasses.TestConfigThrowsPrimitiveParsingException; import com.tngtech.configbuilder.testclasses.TestConfigThrowsInvocationTargetExceptionException; +import com.tngtech.configbuilder.testclasses.TestConfigThrowsPrimitiveParsingException; import com.tngtech.configbuilder.testclasses.TestConfigWithoutDefaultConstructor; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -import java.util.Arrays; -import java.util.Collection; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@RunWith(Parameterized.class) public class ConfigBuilderExceptionTest { - private Class configClass; - private Class exceptionClass; - private String message; - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Before - public void setUp() { - } - - @Parameterized.Parameters - public static Collection configs() { - return Arrays.asList(new Object[][]{ - {TestConfigWithoutDefaultConstructor.class, NoConstructorFoundException.class, "build()"}, - {TestConfigThrowsInvocationTargetExceptionException.class, ConfigBuilderException.class, "InvocationTargetException"}, - {TestConfigThrowsPrimitiveParsingException.class, PrimitiveParsingException.class, "stringValue"} - }); - } - - public ConfigBuilderExceptionTest(Class configClass, Class exceptionClass, String message) { - this.configClass = configClass; - this.exceptionClass = exceptionClass; - this.message = message; + private static Stream configs() { + return Stream.of( + Arguments.of(TestConfigWithoutDefaultConstructor.class, NoConstructorFoundException.class, "build()"), + Arguments.of(TestConfigThrowsInvocationTargetExceptionException.class, ConfigBuilderException.class, "InvocationTargetException"), + Arguments.of(TestConfigThrowsPrimitiveParsingException.class, PrimitiveParsingException.class, "stringValue")); } - @Test - public void testConfigBuilderExceptions() { - expectedException.expect(exceptionClass); - expectedException.expectMessage(message); + @MethodSource("configs") + @ParameterizedTest + public void testConfigBuilderExceptions(Class configClass, Class exceptionClass, String message) { ConfigBuilder configBuilder = new ConfigBuilder(configClass); - configBuilder.build(); + assertThatThrownBy(() -> configBuilder.build()) + .isInstanceOf(exceptionClass) + .hasMessageContaining(message); } } diff --git a/src/test/java/com/tngtech/configbuilder/ConfigBuilderExtensionIntegrationTest.java b/src/test/java/com/tngtech/configbuilder/ConfigBuilderExtensionIntegrationTest.java new file mode 100644 index 00000000..3355c807 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/ConfigBuilderExtensionIntegrationTest.java @@ -0,0 +1,26 @@ +package com.tngtech.configbuilder; + +import com.tngtech.configbuilder.testclasses.ExtendedTestConfig; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ConfigBuilderExtensionIntegrationTest { + + private final ExtendedTestConfig config = new ConfigBuilder<>(ExtendedTestConfig.class).build(); + + @Test + public void testGetValuePresentInSuperClassAndCurrentClass() { + assertThat(config.getSomeNumber()).isEqualTo(5); + } + + @Test + public void testGetValueFromSuperclass() { + assertThat(config.getSuperSomeNumber()).isEqualTo(3); + } + + @Test + public void testDirectValue() { + assertThat(config.getAdditionalNumber()).isEqualTo(4); + } +} diff --git a/src/test/java/com/tngtech/configbuilder/ConfigBuilderFeatureIntegrationTest.java b/src/test/java/com/tngtech/configbuilder/ConfigBuilderFeatureIntegrationTest.java new file mode 100644 index 00000000..a608a659 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/ConfigBuilderFeatureIntegrationTest.java @@ -0,0 +1,23 @@ +package com.tngtech.configbuilder; + +import com.tngtech.configbuilder.testclasses.TestConfigPropertyNamePrefix; +import com.tngtech.configbuilder.testclasses.TestConfigWithoutDefaultConstructor; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ConfigBuilderFeatureIntegrationTest { + @Test + public void testConfigBuilderWithConstructorArgument() { + TestConfigWithoutDefaultConstructor c = ConfigBuilder.on(TestConfigWithoutDefaultConstructor.class).build(3); + + assertThat(c.getNumber()).isEqualTo(3); + } + + @Test + public void testConfigBuilderWithPropertyNamePrefix() { + TestConfigPropertyNamePrefix config = ConfigBuilder.on(TestConfigPropertyNamePrefix.class).build(); + + assertThat(config.getFoo()).isEqualTo("first property"); + } +} diff --git a/src/test/java/com/tngtech/configbuilder/ConfigBuilderIntegrationTest.java b/src/test/java/com/tngtech/configbuilder/ConfigBuilderIntegrationTest.java index 1b38026c..2118e24f 100644 --- a/src/test/java/com/tngtech/configbuilder/ConfigBuilderIntegrationTest.java +++ b/src/test/java/com/tngtech/configbuilder/ConfigBuilderIntegrationTest.java @@ -1,111 +1,88 @@ package com.tngtech.configbuilder; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import com.tngtech.configbuilder.exception.ValidatorException; import com.tngtech.configbuilder.testclasses.TestConfig; -import com.tngtech.configbuilder.testclasses.TestConfigWithoutDefaultConstructor; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; +import com.tngtech.configbuilder.testutil.SystemOutExtension; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; +import java.util.List; +import javax.validation.constraints.NotNull; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals; +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Sets.newHashSet; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; -@RunWith(Parameterized.class) public class ConfigBuilderIntegrationTest { - private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); - - private Class configClass; - private Object configInstance; - - @Before - public void setUp() { - System.setOut(new PrintStream(outContent)); - } + @RegisterExtension + static SystemOutExtension systemOut = new SystemOutExtension(); - @After - public void tearDown() { - System.setOut(null); - } - - @Parameterized.Parameters - public static Collection configs() { - TestConfig testConfig = new TestConfig(); - testConfig.setSomeString("Hello, World!"); - testConfig.setSomeNumber(3); - testConfig.setBoolean(true); - testConfig.setStringCollection(Lists.newArrayList("first entry","second entry")); - testConfig.setIntegerList(Lists.newArrayList(1,2,3,4,5)); - testConfig.setPathCollection(Sets.newHashSet(Paths.get("/etc"), Paths.get("/usr"))); - testConfig.setHomeDir(Paths.get(System.getenv("HOME"))); - testConfig.setSystemProperty(System.getProperty("user.language")); - - return Arrays.asList(new Object[][]{{TestConfig.class, testConfig}}); - } - - public ConfigBuilderIntegrationTest(Class configClass, Object configInstance) { - this.configClass = configClass; - this.configInstance = configInstance; - } + private final ConfigBuilder configBuilder = ConfigBuilder.on(TestConfig.class); @Test public void testConfigBuilderWithParameters() { - ConfigBuilder configBuilder = new ConfigBuilder(configClass); - String[] args = new String[]{"-u", "--collection", "first entry,second entry"}; + TestConfig expectedTestConfig = new TestConfig(); + expectedTestConfig.setSomeString("Hello, World!"); + expectedTestConfig.setSomeNumber(3); + expectedTestConfig.setBoolean(true); + expectedTestConfig.setStringCollection(newArrayList("first entry", "second entry")); + expectedTestConfig.setIntegerList(newArrayList(1, 2, 3, 4, 5)); + expectedTestConfig.setPathCollection(newHashSet(Paths.get("/etc"), Paths.get("/usr"))); + expectedTestConfig.setHomeDir(Paths.get(System.getenv("HOME"))); + expectedTestConfig.setSystemProperty(System.getProperty("user.language")); + + String[] args = {"-u", "--collection", "first entry,second entry"}; Object result = configBuilder.withCommandLineArgs(args).build(); - assertReflectionEquals(configInstance, result); - assertTrue(outContent.toString().contains("config validated")); - } - @Test - public void testConfigBuilderWithConstructorArgument() { - ConfigBuilder configBuilder = new ConfigBuilder<>(TestConfigWithoutDefaultConstructor.class); - TestConfigWithoutDefaultConstructor c = configBuilder.build(3); - assertEquals(3, c.getNumber()); + assertThat(result).usingRecursiveComparison().isEqualTo(expectedTestConfig); + assertThat(systemOut.getLog()).contains("config validated"); } @Test public void testWithImportedConfig() { - ArrayList arrayList = Lists.newArrayList("collection", "two"); + ArrayList arrayList = newArrayList("collection", "two"); String home = System.getenv("HOME"); String userLanguage = System.getProperty("user.language"); TestConfig importedTestConfig = new TestConfig(); importedTestConfig.setSomeNumber(5); - importedTestConfig.setStringCollection(Lists.newArrayList("/mnt","/home")); + importedTestConfig.setStringCollection(newArrayList("/mnt", "/home")); TestConfig expectedTestConfig = new TestConfig(); expectedTestConfig.setSomeNumber(5); - expectedTestConfig.setPathCollection(Sets.newHashSet(Paths.get("/mnt"), Paths.get("/home"))); + List paths = Arrays.asList(Paths.get("/mnt"), Paths.get("/home")); + expectedTestConfig.setPathCollection(newHashSet(paths)); expectedTestConfig.setCopiedStringCollection(importedTestConfig.getStringCollection()); expectedTestConfig.setSomeString("Hello, World!"); expectedTestConfig.setBoolean(true); expectedTestConfig.setStringCollection(arrayList); - expectedTestConfig.setIntegerList(Lists.newArrayList(1, 2, 3, 4, 5)); + expectedTestConfig.setIntegerList(newArrayList(1, 2, 3, 4, 5)); expectedTestConfig.setHomeDir(Paths.get(home)); expectedTestConfig.setSystemProperty(userLanguage); + String[] args = {"-u", "--collection", "collection,two"}; + Object result = configBuilder.withCommandLineArgs(args).withImportedConfiguration(importedTestConfig).build(); + + assertThat(result).usingRecursiveComparison().isEqualTo(expectedTestConfig); + assertThat(systemOut.getLog()).contains("config validated"); + } - ConfigBuilder configBuilder = new ConfigBuilder(configClass); - String[] args = new String[]{"-u", "--collection", "collection,two"}; + static class ConfigWithNotNullValidation { + @NotNull + private String notNullString; + } - Object result = configBuilder.withCommandLineArgs(args).withImportedConfiguration(importedTestConfig).build(); - assertReflectionEquals(expectedTestConfig, result); - assertTrue(outContent.toString().contains("config validated")); + @Test + public void testValidation(){ + ConfigBuilder configBuilder = new ConfigBuilder<>(ConfigWithNotNullValidation.class); + assertThatThrownBy(() -> configBuilder.build()) + .isInstanceOf(ValidatorException.class) + .hasMessageContaining("must not be null"); } } diff --git a/src/test/java/com/tngtech/configbuilder/ConfigBuilderLoggingTest.java b/src/test/java/com/tngtech/configbuilder/ConfigBuilderLoggingTest.java new file mode 100644 index 00000000..d988681e --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/ConfigBuilderLoggingTest.java @@ -0,0 +1,43 @@ +package com.tngtech.configbuilder; + +import com.tngtech.configbuilder.annotation.configuration.DoNotLogValue; +import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; +import com.tngtech.configbuilder.testutil.LoggerExtension; +import java.util.Properties; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ConfigBuilderLoggingTest { + + @RegisterExtension + static LoggerExtension logger = new LoggerExtension(); + + @Test + public void testValueLogging() { + Properties properties = new Properties(); + properties.put("username", "johndoe"); + properties.put("password", "test123"); + + Config config = ConfigBuilder.on(Config.class) + .addProperties(properties) + .build(); + + assertThat(config.username).isEqualTo("johndoe"); + assertThat(config.password).isEqualTo("test123"); + assertThat(logger.getLog()) + .contains("found value \"johndoe\" for field username") + .doesNotContain("test123") + .contains("found value for field password"); + } + + static class Config { + @PropertyValue("username") + String username; + + @DoNotLogValue + @PropertyValue("password") + String password; + } +} diff --git a/src/test/java/com/tngtech/configbuilder/ConfigBuilderTest.java b/src/test/java/com/tngtech/configbuilder/ConfigBuilderTest.java index 6f59e10f..5b2de2e1 100644 --- a/src/test/java/com/tngtech/configbuilder/ConfigBuilderTest.java +++ b/src/test/java/com/tngtech/configbuilder/ConfigBuilderTest.java @@ -1,40 +1,51 @@ package com.tngtech.configbuilder; -import com.google.common.collect.Lists; -import com.tngtech.configbuilder.annotation.configuration.LoadingOrder; import com.tngtech.configbuilder.configuration.BuilderConfiguration; import com.tngtech.configbuilder.configuration.ErrorMessageSetup; -import com.tngtech.configbuilder.util.ConfigBuilderFactory; import com.tngtech.configbuilder.testclasses.TestConfig; -import com.tngtech.configbuilder.util.*; +import com.tngtech.configbuilder.testutil.SystemOutExtension; +import com.tngtech.configbuilder.util.CommandLineHelper; +import com.tngtech.configbuilder.util.ConfigBuilderFactory; +import com.tngtech.configbuilder.util.ConfigValidator; +import com.tngtech.configbuilder.util.ConstructionHelper; +import com.tngtech.configbuilder.util.FieldSetter; +import com.tngtech.configbuilder.util.PropertyLoaderConfigurator; import com.tngtech.propertyloader.PropertyLoader; -import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.Options; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Matchers; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; +import com.tngtech.propertyloader.impl.DefaultPropertyFilterContainer; +import com.tngtech.propertyloader.impl.DefaultPropertyLocationContainer; +import com.tngtech.propertyloader.impl.DefaultPropertySuffixContainer; +import com.tngtech.propertyloader.impl.filters.DecryptingFilter; +import com.tngtech.propertyloader.impl.filters.VariableResolvingFilter; +import com.tngtech.propertyloader.impl.interfaces.PropertyLoaderFilter; import java.util.List; import java.util.Properties; +import org.apache.commons.cli.Options; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static com.google.common.collect.Lists.newArrayList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.isA; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.same; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class ConfigBuilderTest { - private ConfigBuilder configBuilder; + @RegisterExtension + static SystemOutExtension systemOut = new SystemOutExtension(); - private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private ConfigBuilder configBuilder; @Mock private ConfigBuilderFactory configBuilderFactory; @@ -50,26 +61,27 @@ public class ConfigBuilderTest { private ErrorMessageSetup errorMessageSetup; @Mock private ConstructionHelper constructionHelper; - + @Mock + private Properties additionalProperties; @Mock private PropertyLoaderConfigurator propertyLoaderConfigurator; - @Mock private PropertyLoader propertyLoader; @Mock - private Options commandLineOptions; + private DefaultPropertySuffixContainer suffixContainer; @Mock - private CommandLine commandLine; + private DefaultPropertyLocationContainer locationContainer; @Mock - private Properties properties; + private DefaultPropertyFilterContainer filterContainer; @Mock - private LoadingOrder loadingOrder; - - @Before - public void setUp() throws Exception { - - System.setOut(new PrintStream(outContent)); + private List filters; + @Mock + private Options commandLineOptions; + @Mock + private Properties properties; + @BeforeEach + public void setUp() { when(configBuilderFactory.getInstance(BuilderConfiguration.class)).thenReturn(builderConfiguration); when(configBuilderFactory.getInstance(CommandLineHelper.class)).thenReturn(commandLineHelper); when(configBuilderFactory.getInstance(ConfigValidator.class)).thenReturn(configValidator); @@ -77,58 +89,175 @@ public void setUp() throws Exception { when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(errorMessageSetup); when(configBuilderFactory.getInstance(ConstructionHelper.class)).thenReturn(constructionHelper); when(configBuilderFactory.getInstance(PropertyLoaderConfigurator.class)).thenReturn(propertyLoaderConfigurator); + when(configBuilderFactory.createInstance(Properties.class)).thenReturn(additionalProperties); when(propertyLoaderConfigurator.configurePropertyLoader(TestConfig.class)).thenReturn(propertyLoader); - when(commandLineHelper.getOptions(TestConfig.class)).thenReturn(commandLineOptions); configBuilder = new ConfigBuilder<>(TestConfig.class, configBuilderFactory); } - @After - public void tearDown() { - System.setOut(null); - } - @Test - public void testWithCommandLineArgs() throws Exception { - when(commandLineHelper.getCommandLine(TestConfig.class, new String[]{})).thenReturn(commandLine); - assertEquals(configBuilder,configBuilder.withCommandLineArgs(new String[]{})); + public void testWithCommandLineArgs() { + assertThat(configBuilder.withCommandLineArgs(new String[]{})).isSameAs(configBuilder); } @Test - public void testOverridePropertiesFiles() throws Exception { - List baseNames = Lists.newArrayList("file"); + public void testOverridePropertiesFiles() { + List baseNames = newArrayList("file"); configBuilder.overridePropertiesFiles(baseNames); verify(propertyLoader).withBaseNames(baseNames); } @Test - public void testPrintCommandLineHelp() throws Exception { + public void testPrintCommandLineHelp() { + when(commandLineHelper.getOptions(TestConfig.class)).thenReturn(commandLineOptions); + configBuilder.printCommandLineHelp(); - assertTrue(outContent.toString().contains("Command Line Options for class TestConfig")); + assertThat(systemOut.getLog()).contains("Command Line Options for class TestConfig"); } @Test - public void testBuild() throws Exception { + public void testBuild() { when(propertyLoader.load()).thenReturn(properties); + TestConfig testConfig = new TestConfig(); + when(constructionHelper.getInstance(TestConfig.class)).thenReturn(testConfig); + configBuilder.build(); + verify(propertyLoader).load(); verify(builderConfiguration).setProperties(properties); verify(errorMessageSetup).initialize(null, propertyLoader); - - verify(fieldSetter).setFields(Matchers.any(TestConfig.class), Matchers.any(BuilderConfiguration.class)); - verify(configValidator).validate(Matchers.any(TestConfig.class)); + verify(fieldSetter).setFields(same(testConfig), any(BuilderConfiguration.class)); + verify(configValidator).validate(same(testConfig)); } -// @Test -// This test does not work. It needs to be fixed. Urgently. Please do it. Only you can save us! - public void testMerge() throws Exception { + @Test + public void testMerge() { TestConfig importedConfig = new TestConfig(); when(propertyLoader.load()).thenReturn(properties); - + configBuilder.withImportedConfiguration(importedConfig).build(); - + verify(propertyLoader).load(); verify(builderConfiguration).setProperties(properties); verify(errorMessageSetup).initialize(null, propertyLoader); } + + @Test + public void testAddProperties() { + Properties properties = mock(Properties.class); + assertThat(configBuilder.addProperties(properties)).isSameAs(configBuilder); + + verify(additionalProperties).putAll(properties); + verifyNoMoreInteractions(propertyLoader); + } + + @Test + public void testWithExtension() { + final String propertyExtension = ""; + assertThat(configBuilder.withPropertyExtension(propertyExtension)).isSameAs(configBuilder); + + verify(propertyLoader).withExtension(propertyExtension); + verifyNoMoreInteractions(propertyLoader); + } + + @Test + public void testWithPropertySuffix() { + when(propertyLoader.getSuffixes()).thenReturn(suffixContainer); + + final String propertySuffix = ""; + assertThat(configBuilder.withPropertySuffix(propertySuffix)).isSameAs(configBuilder); + + InOrder order = inOrder(propertyLoader, suffixContainer); + order.verify(propertyLoader).getSuffixes(); + order.verify(suffixContainer).clear(); + order.verify(suffixContainer).addSuffixList(newArrayList(propertySuffix)); + order.verifyNoMoreInteractions(); + } + + @Test + public void testWithPropertySuffixes() { + when(propertyLoader.getSuffixes()).thenReturn(suffixContainer); + final String propertySuffix1 = ""; + final String propertySuffix2 = ""; + + assertThat(configBuilder.withPropertySuffixes(propertySuffix1, propertySuffix2)).isSameAs(configBuilder); + + InOrder order = inOrder(propertyLoader, suffixContainer); + order.verify(propertyLoader).getSuffixes(); + order.verify(suffixContainer).clear(); + order.verify(suffixContainer).addSuffixList(newArrayList(propertySuffix1, propertySuffix2)); + order.verifyNoMoreInteractions(); + } + + @Test + public void testAddPropertySuffixes() { + when(propertyLoader.getSuffixes()).thenReturn(suffixContainer); + + final String propertySuffix1 = ""; + final String propertySuffix2 = ""; + assertThat(configBuilder.addPropertySuffixes(propertySuffix1, propertySuffix2)).isSameAs(configBuilder); + + InOrder order = inOrder(propertyLoader, suffixContainer); + order.verify(propertyLoader).getSuffixes(); + order.verify(suffixContainer).addSuffixList(newArrayList(propertySuffix1, propertySuffix2)); + order.verifyNoMoreInteractions(); + } + + @Test + public void testWithPropertiesFile() { + final String propertyFile = ""; + assertThat(configBuilder.withPropertiesFile(propertyFile)).isSameAs(configBuilder); + + verify(propertyLoader).withBaseNames(newArrayList(propertyFile)); + verifyNoMoreInteractions(propertyLoader); + } + + @Test + public void testWithPropertiesFiles() { + final String propertiesFile1 = ""; + final String propertiesFile2 = ""; + assertThat(configBuilder.withPropertiesFiles(propertiesFile1, propertiesFile2)).isSameAs(configBuilder); + + verify(propertyLoader).withBaseNames(newArrayList(propertiesFile1, propertiesFile2)); + verifyNoMoreInteractions(propertyLoader); + } + + @Test + public void testWithPropertyLocations() { + when(propertyLoader.getLocations()).thenReturn(locationContainer); + + final String propertyLocation1 = ""; + final Class propertyLocation2 = PropertyLoader.class; + assertThat(configBuilder.withPropertyLocations(propertyLocation1, propertyLocation2)).isSameAs(configBuilder); + + InOrder order = inOrder(propertyLoader, locationContainer); + order.verify(propertyLoader).getLocations(); + order.verify(locationContainer).clear(); + order.verify(locationContainer).atDirectory(propertyLocation1); + order.verify(locationContainer).atRelativeToClass(propertyLocation2); + order.verifyNoMoreInteractions(); + } + + @Test + public void testWithPropertyFilters() { + when(propertyLoader.getFilters()).thenReturn(filterContainer); + when(filterContainer.getFilters()).thenReturn(filters); + + assertThat(configBuilder.withPropertyFilters(VariableResolvingFilter.class, DecryptingFilter.class)).isSameAs(configBuilder); + + InOrder order = inOrder(propertyLoader, filterContainer, filters); + order.verify(propertyLoader).getFilters(); + order.verify(filterContainer).getFilters(); + order.verify(filters).clear(); + order.verify(filters).add(isA(VariableResolvingFilter.class)); + order.verify(filters).add(isA(DecryptingFilter.class)); + order.verifyNoMoreInteractions(); + } + + @Test + public void testStaticBuilderFactoryMethod() { + configBuilder = ConfigBuilder.on(TestConfig.class); + + assertThat(configBuilder).isNotNull(); + } } diff --git a/src/test/java/com/tngtech/configbuilder/ConfigBuilderWithoutAnnotationsIntegrationTest.java b/src/test/java/com/tngtech/configbuilder/ConfigBuilderWithoutAnnotationsIntegrationTest.java new file mode 100644 index 00000000..9f756a00 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/ConfigBuilderWithoutAnnotationsIntegrationTest.java @@ -0,0 +1,52 @@ +package com.tngtech.configbuilder; + +import com.tngtech.configbuilder.testclasses.TestConfigWithoutAnnotations; +import com.tngtech.configbuilder.testutil.SystemOutExtension; +import com.tngtech.propertyloader.PropertyLoader; +import com.tngtech.propertyloader.impl.filters.DecryptingFilter; +import java.nio.file.Paths; +import java.util.Properties; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Sets.newHashSet; +import static org.assertj.core.api.Assertions.assertThat; + +public class ConfigBuilderWithoutAnnotationsIntegrationTest { + + @RegisterExtension + static SystemOutExtension systemOut = new SystemOutExtension(); + + private final ConfigBuilder configBuilder = new ConfigBuilder<>(TestConfigWithoutAnnotations.class); + + @Test + public void testConfigBuilderWithParameters() { + TestConfigWithoutAnnotations expectedConfig = new TestConfigWithoutAnnotations(); + expectedConfig.setSomeString("Hello, Galaxy!"); + expectedConfig.setOtherString("${a}"); + expectedConfig.setSomeNumber(3); + expectedConfig.setBoolean(true); + expectedConfig.setStringCollection(newArrayList("first entry", "second entry")); + expectedConfig.setIntegerList(newArrayList(1, 2, 3, 4, 5)); + expectedConfig.setPathCollection(newHashSet(Paths.get("/etc"), Paths.get("/usr"))); + expectedConfig.setHomeDir(Paths.get(System.getenv("HOME"))); + expectedConfig.setSystemProperty(System.getProperty("user.language")); + + final Properties additionalProperties = new Properties(); + additionalProperties.put("a", "Hello, Galaxy!"); + String[] args = {"-u", "--collection", "first entry,second entry"}; + + Object result = configBuilder + .withPropertyLocations(PropertyLoader.class) + .withPropertyFilters(DecryptingFilter.class) + .withPropertyExtension("testproperties") + .withPropertySuffix("test") + .withPropertiesFile("demoapp-configuration") + .withCommandLineArgs(args) + .addProperties(additionalProperties) + .build(); + assertThat(result).usingRecursiveComparison().isEqualTo(expectedConfig); + assertThat(systemOut.getLog()).contains("config validated"); + } +} diff --git a/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertiesFilesProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertiesFilesProcessorTest.java index a2a8dfd5..8d19fbeb 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertiesFilesProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertiesFilesProcessorTest.java @@ -1,40 +1,32 @@ package com.tngtech.configbuilder.annotation.propertyloaderconfiguration; -import com.google.common.collect.Lists; import com.tngtech.propertyloader.PropertyLoader; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; +import static com.google.common.collect.Lists.newArrayList; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class PropertiesFilesProcessorTest { @Mock private PropertiesFiles propertiesFiles; @Mock - PropertyLoader propertyLoader; + private PropertyLoader propertyLoader; - private PropertiesFilesProcessor propertiesFilesProcessor; - - @Before - public void setUp() throws Exception { - propertiesFilesProcessor = new PropertiesFilesProcessor(); - } + private final PropertiesFilesProcessor propertiesFilesProcessor = new PropertiesFilesProcessor(); @Test public void testPropertiesFilesProcessor() { - - String[] fileNames = new String[]{"file1", "file2"}; - + String[] fileNames = {"file1", "file2"}; when(propertiesFiles.value()).thenReturn(fileNames); propertiesFilesProcessor.configurePropertyLoader(propertiesFiles, propertyLoader); - verify(propertyLoader).withBaseNames(Lists.newArrayList(fileNames)); + verify(propertyLoader).withBaseNames(newArrayList(fileNames)); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyExtensionProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyExtensionProcessorTest.java index 77d9ef11..0042473b 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyExtensionProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyExtensionProcessorTest.java @@ -1,34 +1,26 @@ package com.tngtech.configbuilder.annotation.propertyloaderconfiguration; import com.tngtech.propertyloader.PropertyLoader; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class PropertyExtensionProcessorTest { - private PropertyExtensionProcessor propertyExtensionProcessor; + private final PropertyExtensionProcessor propertyExtensionProcessor = new PropertyExtensionProcessor(); @Mock private PropertyExtension propertyExtension; @Mock - PropertyLoader propertyLoader; - - @Before - public void setUp() throws Exception { - propertyExtensionProcessor = new PropertyExtensionProcessor(); - } + private PropertyLoader propertyLoader; @Test public void testPropertyExtensionProcessor() { - - when(propertyExtension.value()).thenReturn("extension"); propertyExtensionProcessor.configurePropertyLoader(propertyExtension, propertyLoader); diff --git a/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyFiltersProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyFiltersProcessorTest.java new file mode 100644 index 00000000..8932ee75 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyFiltersProcessorTest.java @@ -0,0 +1,91 @@ +package com.tngtech.configbuilder.annotation.propertyloaderconfiguration; + +import com.tngtech.propertyloader.PropertyLoader; +import com.tngtech.propertyloader.impl.DefaultPropertyFilterContainer; +import com.tngtech.propertyloader.impl.filters.DecryptingFilter; +import com.tngtech.propertyloader.impl.filters.EnvironmentResolvingFilter; +import com.tngtech.propertyloader.impl.filters.ThrowIfPropertyHasToBeDefined; +import com.tngtech.propertyloader.impl.filters.ValueModifyingFilter; +import com.tngtech.propertyloader.impl.filters.VariableResolvingFilter; +import com.tngtech.propertyloader.impl.filters.WarnOnSurroundingWhitespace; +import com.tngtech.propertyloader.impl.interfaces.PropertyLoaderFilter; +import java.util.Properties; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class PropertyFiltersProcessorTest { + + private static class TestPropertyFilter extends ValueModifyingFilter { + @Override + protected String filterValue(String key, String value, Properties properties) { + return null; + } + } + + @Mock + private PropertyFilters propertyFilters; + @Mock + private DefaultPropertyFilterContainer filterContainer; + @Mock + private PropertyLoader propertyLoader; + + private final PropertyFiltersProcessor propertyFiltersProcessor = new PropertyFiltersProcessor(); + + @BeforeEach + public void setUp() { + when(propertyLoader.getFilters()).thenReturn(filterContainer); + } + + @Test + public void testAnnotationWithNoValues() { + @SuppressWarnings("unchecked") Class[] classes = new Class[0]; + when(propertyFilters.value()).thenReturn(classes); + + propertyFiltersProcessor.configurePropertyLoader(propertyFilters, propertyLoader); + + verify(filterContainer).clear(); + } + + @Test + public void testAnnotationWithSomeValues() { + @SuppressWarnings("unchecked") Class[] classes = new Class[]{ + VariableResolvingFilter.class, + DecryptingFilter.class, + EnvironmentResolvingFilter.class, + WarnOnSurroundingWhitespace.class, + ThrowIfPropertyHasToBeDefined.class + }; + when(propertyFilters.value()).thenReturn(classes); + + propertyFiltersProcessor.configurePropertyLoader(propertyFilters, propertyLoader); + + InOrder order = inOrder(filterContainer); + order.verify(filterContainer).clear(); + order.verify(filterContainer).withVariableResolvingFilter(); + order.verify(filterContainer).withDecryptingFilter(); + order.verify(filterContainer).withEnvironmentResolvingFilter(); + order.verify(filterContainer).withWarnOnSurroundingWhitespace(); + order.verify(filterContainer).withWarnIfPropertyHasToBeDefined(); + order.verifyNoMoreInteractions(); + } + + @Test + public void testAnnotationWithUnknownValuesShouldThrowException() { + @SuppressWarnings("unchecked") Class[] classes = new Class[]{TestPropertyFilter.class}; + when(propertyFilters.value()).thenReturn(classes); + + assertThatThrownBy(() -> propertyFiltersProcessor.configurePropertyLoader(propertyFilters, propertyLoader)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("unhandled filter class TestPropertyFilter"); + } +} diff --git a/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocationsProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocationsProcessorTest.java index b2880cf5..60f149f3 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocationsProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertyLocationsProcessorTest.java @@ -2,38 +2,31 @@ import com.tngtech.propertyloader.PropertyLoader; import com.tngtech.propertyloader.impl.DefaultPropertyLocationContainer; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class PropertyLocationsProcessorTest { - private PropertyLocationsProcessor propertyLocationsProcessor; + private final PropertyLocationsProcessor propertyLocationsProcessor = new PropertyLocationsProcessor(); @Mock private PropertyLocations propertyLocations; @Mock - DefaultPropertyLocationContainer propertyLocation; + private DefaultPropertyLocationContainer propertyLocation; @Mock - PropertyLoader propertyLoader; - - @Before - public void setUp() throws Exception { - propertyLocationsProcessor = new PropertyLocationsProcessor(); - } + private PropertyLoader propertyLoader; + @SuppressWarnings("rawtypes") @Test public void testPropertyLocationsProcessor() { - - String[] dirs = new String[]{"dir1", "dir2"}; - Class[] classes = new Class[]{this.getClass(), Object.class}; - + String[] dirs = {"dir1", "dir2"}; + Class[] classes = {this.getClass(), Object.class}; when(propertyLoader.getLocations()).thenReturn(propertyLocation); when(propertyLocations.directories()).thenReturn(dirs); when(propertyLocations.resourcesForClasses()).thenReturn(classes); @@ -50,7 +43,6 @@ public void testPropertyLocationsProcessor() { @Test public void testPropertyLocationsProcessorWithClassLoader() { - when(propertyLoader.getLocations()).thenReturn(propertyLocation); when(propertyLocations.directories()).thenReturn(new String[]{}); when(propertyLocations.resourcesForClasses()).thenReturn(new Class[]{}); diff --git a/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertySuffixProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertySuffixProcessorTest.java index 72a7b311..a62cd7b1 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertySuffixProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/propertyloaderconfiguration/PropertySuffixProcessorTest.java @@ -2,37 +2,29 @@ import com.tngtech.propertyloader.PropertyLoader; import com.tngtech.propertyloader.impl.DefaultPropertySuffixContainer; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class PropertySuffixProcessorTest { - private PropertySuffixProcessor propertySuffixProcessor; + private final PropertySuffixProcessor propertySuffixProcessor = new PropertySuffixProcessor(); @Mock private PropertySuffixes propertySuffixes; @Mock - DefaultPropertySuffixContainer propertySuffix; + private DefaultPropertySuffixContainer propertySuffix; @Mock - PropertyLoader propertyLoader; - - @Before - public void setUp() throws Exception { - propertySuffixProcessor = new PropertySuffixProcessor(); - } + private PropertyLoader propertyLoader; @Test public void testPropertySuffixProcessor() { - - String[] suffixes = new String[]{"suffix1", "suffix2"}; - + String[] suffixes = {"suffix1", "suffix2"}; when(propertyLoader.getSuffixes()).thenReturn(propertySuffix); when(propertySuffixes.extraSuffixes()).thenReturn(suffixes); when(propertySuffixes.hostNames()).thenReturn(false); @@ -46,7 +38,6 @@ public void testPropertySuffixProcessor() { @Test public void testPropertySuffixProcessorWithHostNames() { - when(propertyLoader.getSuffixes()).thenReturn(propertySuffix); when(propertySuffixes.extraSuffixes()).thenReturn(new String[]{}); when(propertySuffixes.hostNames()).thenReturn(true); diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringListTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringListTransformerTest.java index a31a6481..17ae1ef3 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringListTransformerTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringListTransformerTest.java @@ -1,74 +1,54 @@ package com.tngtech.configbuilder.annotation.typetransformer; -import com.google.common.collect.Lists; import com.tngtech.configbuilder.util.ConfigBuilderFactory; import com.tngtech.configbuilder.util.FieldValueTransformer; -import com.tngtech.configbuilder.util.FieldValueTransformerComponentTest; import com.tngtech.configbuilder.util.GenericsAndCastingHelper; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -import java.lang.reflect.ParameterizedType; -import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static com.google.common.collect.Lists.newArrayList; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class CharacterSeparatedStringToStringListTransformerTest { - private CharacterSeparatedStringToStringListTransformer transformer; + + private final CharacterSeparatedStringToStringListTransformer transformer = new CharacterSeparatedStringToStringListTransformer(); @Mock private FieldValueTransformer fieldValueTransformer; @Mock private ConfigBuilderFactory configBuilderFactory; - @Mock - private GenericsAndCastingHelper genericsAndCastingHelper; - - @Before - public void setUp() { - transformer = new CharacterSeparatedStringToStringListTransformer(); - } @Test public void testTransformer() { - Collection expectedResult = Lists.newArrayList("Wayne","André","Kanye","Lebron"); + Collection expectedResult = newArrayList("Wayne", "André", "Kanye", "Lebron"); transformer.initialize(fieldValueTransformer, configBuilderFactory, ","); Collection actualResult = transformer.transform("Wayne,André,Kanye,Lebron"); - assertThat(actualResult,equalTo(expectedResult)); + assertThat(actualResult).isEqualTo(expectedResult); transformer.initialize(fieldValueTransformer, configBuilderFactory, ";"); actualResult = transformer.transform("Wayne;André;Kanye;Lebron"); - assertThat(actualResult,equalTo(expectedResult)); + assertThat(actualResult).isEqualTo(expectedResult); } @Test - public void testIsMatching() throws Exception { + public void testIsMatching() { initializeFactoryAndHelperMocks(); transformer.initialize(fieldValueTransformer, configBuilderFactory, ","); - assertTrue(transformer.isMatching(String.class, List.class)); - assertTrue(transformer.isMatching(String.class, Collection.class)); - assertFalse(transformer.isMatching(String.class, Set.class)); - assertFalse(transformer.isMatching(Object.class, List.class)); + assertThat(transformer.isMatching(String.class, List.class)).isTrue(); + assertThat(transformer.isMatching(String.class, Collection.class)).isTrue(); + assertThat(transformer.isMatching(String.class, Set.class)).isFalse(); + assertThat(transformer.isMatching(Object.class, List.class)).isFalse(); } - private void initializeFactoryAndHelperMocks(){ - when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(genericsAndCastingHelper); - - when(genericsAndCastingHelper.castTypeToClass(List.class)).thenReturn((Class)List.class); - when(genericsAndCastingHelper.castTypeToClass(String.class)).thenReturn((Class)String.class); - when(genericsAndCastingHelper.castTypeToClass(Collection.class)).thenReturn((Class)Collection.class); - when(genericsAndCastingHelper.castTypeToClass(Object.class)).thenReturn((Class)Object.class); - when(genericsAndCastingHelper.castTypeToClass(((ParameterizedType)(transformer.getClass().getGenericSuperclass())).getActualTypeArguments()[1])).thenReturn((Class)List.class); + private void initializeFactoryAndHelperMocks() { + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringSetTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringSetTransformerTest.java index f84e74cb..802ed8a3 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringSetTransformerTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CharacterSeparatedStringToStringSetTransformerTest.java @@ -1,70 +1,53 @@ package com.tngtech.configbuilder.annotation.typetransformer; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import com.tngtech.configbuilder.util.ConfigBuilderFactory; import com.tngtech.configbuilder.util.FieldValueTransformer; import com.tngtech.configbuilder.util.GenericsAndCastingHelper; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import java.util.Collection; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; -import java.lang.reflect.ParameterizedType; -import java.util.*; - -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static com.google.common.collect.Sets.newHashSet; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class CharacterSeparatedStringToStringSetTransformerTest { - private CharacterSeparatedStringToStringSetTransformer transformer; + + private final CharacterSeparatedStringToStringSetTransformer transformer = new CharacterSeparatedStringToStringSetTransformer(); @Mock private FieldValueTransformer fieldValueTransformer; @Mock private ConfigBuilderFactory configBuilderFactory; - @Mock - private GenericsAndCastingHelper genericsAndCastingHelper; - - @Before - public void setUp() { - transformer = new CharacterSeparatedStringToStringSetTransformer(); - } @Test public void testTransformer() { - Set expectedResult = Sets.newHashSet("Wayne", "André", "Kanye", "Lebron"); + Set expectedResult = newHashSet("Wayne", "André", "Kanye", "Lebron"); transformer.initialize(fieldValueTransformer, configBuilderFactory, ","); Set actualResult = transformer.transform("Wayne,André,Kanye,Lebron"); - assertThat(actualResult,equalTo(expectedResult)); + assertThat(actualResult).isEqualTo(expectedResult); transformer.initialize(fieldValueTransformer, configBuilderFactory, ";"); actualResult = transformer.transform("Wayne;André;Kanye;Lebron"); - assertThat(actualResult,equalTo(expectedResult)); + assertThat(actualResult).isEqualTo(expectedResult); } @Test - public void testIsMatching() throws Exception { + public void testIsMatching() { initializeFactoryAndHelperMocks(); transformer.initialize(fieldValueTransformer, configBuilderFactory, ","); - assertTrue(transformer.isMatching(String.class, Set.class)); - assertTrue(transformer.isMatching(String.class, Collection.class)); - assertFalse(transformer.isMatching(Object.class, List.class)); + assertThat(transformer.isMatching(String.class, Set.class)).isTrue(); + assertThat(transformer.isMatching(String.class, Collection.class)).isTrue(); + assertThat(transformer.isMatching(Object.class, List.class)).isFalse(); } private void initializeFactoryAndHelperMocks(){ - when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(genericsAndCastingHelper); - - when(genericsAndCastingHelper.castTypeToClass(List.class)).thenReturn((Class)List.class); - when(genericsAndCastingHelper.castTypeToClass(String.class)).thenReturn((Class)String.class); - when(genericsAndCastingHelper.castTypeToClass(Collection.class)).thenReturn((Class)Collection.class); - when(genericsAndCastingHelper.castTypeToClass(Object.class)).thenReturn((Class)Object.class); - when(genericsAndCastingHelper.castTypeToClass(((ParameterizedType)(transformer.getClass().getGenericSuperclass())).getActualTypeArguments()[1])).thenReturn((Class)Set.class); + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToArrayListTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToArrayListTransformerTest.java index de04c456..21b93241 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToArrayListTransformerTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToArrayListTransformerTest.java @@ -1,31 +1,27 @@ package com.tngtech.configbuilder.annotation.typetransformer; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import com.tngtech.configbuilder.configuration.ErrorMessageSetup; import com.tngtech.configbuilder.util.ConfigBuilderFactory; import com.tngtech.configbuilder.util.FieldValueTransformer; import com.tngtech.configbuilder.util.GenericsAndCastingHelper; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Collection; import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static com.google.common.collect.Sets.newHashSet; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class CollectionToArrayListTransformerTest { - private CollectionToArrayListTransformer collectionToArrayListTransformer; + private final CollectionToArrayListTransformer collectionToArrayListTransformer = new CollectionToArrayListTransformer(); @Mock private ParameterizedType type; @@ -35,42 +31,30 @@ public class CollectionToArrayListTransformerTest { private ConfigBuilderFactory configBuilderFactory; @Mock private ErrorMessageSetup errorMessageSetup; - @Mock - private GenericsAndCastingHelper genericsAndCastingHelper; - @Before - public void setUp() throws Exception { + @BeforeEach + public void setUp() { when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(errorMessageSetup); - when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(genericsAndCastingHelper); - - collectionToArrayListTransformer = new CollectionToArrayListTransformer(); + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); collectionToArrayListTransformer.initialize(fieldValueTransformer, configBuilderFactory); collectionToArrayListTransformer.setTargetType(type); } @Test - public void testTransform() throws Exception { - Set input = Sets.newHashSet(1,2,3); + public void testTransform() { + Set input = newHashSet(1, 2, 3); when(type.getActualTypeArguments()).thenReturn(new Class[]{Double.class}); when(fieldValueTransformer.performNecessaryTransformations(1, Double.class)).thenReturn(1.0); when(fieldValueTransformer.performNecessaryTransformations(2, Double.class)).thenReturn(2.0); when(fieldValueTransformer.performNecessaryTransformations(3, Double.class)).thenReturn(3.0); - assertEquals(Lists.newArrayList(1.0,2.0,3.0), collectionToArrayListTransformer.transform(input)); + assertThat(collectionToArrayListTransformer.transform(input)).containsExactly(1.0, 2.0, 3.0); } @Test - public void testIsMatching() throws Exception { + public void testIsMatching() { collectionToArrayListTransformer.initialize(fieldValueTransformer, configBuilderFactory); - initializeFactoryAndHelper(); - - assertTrue(collectionToArrayListTransformer.isMatching(Collection.class, ArrayList.class)); - assertFalse(collectionToArrayListTransformer.isMatching(Collection.class, Double.class)); - } - - private void initializeFactoryAndHelper() { - when(genericsAndCastingHelper.castTypeToClass(Collection.class)).thenReturn((Class)Collection.class); - when(genericsAndCastingHelper.castTypeToClass(ArrayList.class)).thenReturn((Class)ArrayList.class); - when(genericsAndCastingHelper.castTypeToClass(Double.class)).thenReturn((Class)Double.class); + assertThat(collectionToArrayListTransformer.isMatching(Collection.class, ArrayList.class)).isTrue(); + assertThat(collectionToArrayListTransformer.isMatching(Collection.class, Double.class)).isFalse(); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToHashSetTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToHashSetTransformerTest.java index dc733f41..aed91649 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToHashSetTransformerTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/CollectionToHashSetTransformerTest.java @@ -1,30 +1,27 @@ package com.tngtech.configbuilder.annotation.typetransformer; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; import com.tngtech.configbuilder.configuration.ErrorMessageSetup; import com.tngtech.configbuilder.util.ConfigBuilderFactory; import com.tngtech.configbuilder.util.FieldValueTransformer; import com.tngtech.configbuilder.util.GenericsAndCastingHelper; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.*; +import static com.google.common.collect.Sets.newHashSet; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class CollectionToHashSetTransformerTest { - private CollectionToHashSetTransformer collectionToHashSetTransformer; + private final CollectionToHashSetTransformer collectionToHashSetTransformer = new CollectionToHashSetTransformer(); @Mock private ParameterizedType type; @@ -34,45 +31,31 @@ public class CollectionToHashSetTransformerTest { private ConfigBuilderFactory configBuilderFactory; @Mock private ErrorMessageSetup errorMessageSetup; - @Mock - private GenericsAndCastingHelper genericsAndCastingHelper; - @Before - public void setUp() throws Exception { + @BeforeEach + public void setUp() { when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(errorMessageSetup); - when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(genericsAndCastingHelper); - - collectionToHashSetTransformer = new CollectionToHashSetTransformer(); + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); collectionToHashSetTransformer.initialize(fieldValueTransformer, configBuilderFactory); collectionToHashSetTransformer.setTargetType(type); } @Test - public void testTransform() throws Exception { - Set input = Sets.newHashSet(1,2,3); + public void testTransform() { + Set input = newHashSet(1, 2, 3); when(type.getActualTypeArguments()).thenReturn(new Class[]{Double.class}); when(fieldValueTransformer.performNecessaryTransformations(1, Double.class)).thenReturn(1.0); when(fieldValueTransformer.performNecessaryTransformations(2, Double.class)).thenReturn(2.0); when(fieldValueTransformer.performNecessaryTransformations(3, Double.class)).thenReturn(3.0); - assertEquals(Sets.newHashSet(1.0, 2.0, 3.0), collectionToHashSetTransformer.transform(input)); + assertThat(collectionToHashSetTransformer.transform(input)).containsExactly(1.0, 2.0, 3.0); } @Test - public void testIsMatching() throws Exception { + public void testIsMatching() { collectionToHashSetTransformer.initialize(fieldValueTransformer, configBuilderFactory); - initializeFactoryAndHelper(); - - assertTrue(collectionToHashSetTransformer.isMatching(ArrayList.class, Set.class)); - assertFalse(collectionToHashSetTransformer.isMatching(ArrayList.class, ArrayList.class)); - assertFalse(collectionToHashSetTransformer.isMatching(Collection.class, Double.class)); - } - - private void initializeFactoryAndHelper() { - when(genericsAndCastingHelper.castTypeToClass(Collection.class)).thenReturn((Class)Collection.class); - when(genericsAndCastingHelper.castTypeToClass(Set.class)).thenReturn((Class)Set.class); - when(genericsAndCastingHelper.castTypeToClass(HashSet.class)).thenReturn((Class)HashSet.class); - when(genericsAndCastingHelper.castTypeToClass(ArrayList.class)).thenReturn((Class)ArrayList.class); - when(genericsAndCastingHelper.castTypeToClass(Double.class)).thenReturn((Class)Double.class); + assertThat(collectionToHashSetTransformer.isMatching(ArrayList.class, Set.class)).isTrue(); + assertThat(collectionToHashSetTransformer.isMatching(ArrayList.class, ArrayList.class)).isFalse(); + assertThat(collectionToHashSetTransformer.isMatching(Collection.class, Double.class)).isFalse(); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringCollectionToCommaSeparatedStringTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringCollectionToCommaSeparatedStringTransformerTest.java index f1d57968..fc2f135a 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringCollectionToCommaSeparatedStringTransformerTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringCollectionToCommaSeparatedStringTransformerTest.java @@ -1,72 +1,52 @@ package com.tngtech.configbuilder.annotation.typetransformer; -import com.google.common.collect.Lists; import com.tngtech.configbuilder.util.ConfigBuilderFactory; import com.tngtech.configbuilder.util.FieldValueTransformer; import com.tngtech.configbuilder.util.GenericsAndCastingHelper; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -import java.lang.reflect.ParameterizedType; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.Set; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import static com.google.common.collect.Lists.newArrayList; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class StringCollectionToCommaSeparatedStringTransformerTest { - private StringCollectionToCommaSeparatedStringTransformer transformer; + + private final StringCollectionToCommaSeparatedStringTransformer transformer = new StringCollectionToCommaSeparatedStringTransformer(); @Mock private FieldValueTransformer fieldValueTransformer; @Mock private ConfigBuilderFactory configBuilderFactory; - @Mock - private GenericsAndCastingHelper genericsAndCastingHelper; - - @Before - public void setUp() { - transformer = new StringCollectionToCommaSeparatedStringTransformer(); - } @Test public void testTransformer() { - Collection collection = Lists.newArrayList("Rakim","Lakim Shabazz","2Pac"); + Collection collection = newArrayList("Rakim", "Lakim Shabazz", "2Pac"); transformer.initialize(fieldValueTransformer, configBuilderFactory, ","); String actualResult = transformer.transform(collection); - assertThat(actualResult, equalTo("Rakim,Lakim Shabazz,2Pac")); + assertThat(actualResult).isEqualTo("Rakim,Lakim Shabazz,2Pac"); transformer.initialize(fieldValueTransformer, configBuilderFactory, ";"); actualResult = transformer.transform(collection); - assertThat(actualResult, equalTo("Rakim;Lakim Shabazz;2Pac")); + assertThat(actualResult).isEqualTo("Rakim;Lakim Shabazz;2Pac"); } @Test - public void testIsMatching() throws Exception { + public void testIsMatching() { initializeFactoryAndHelperMocks(); transformer.initialize(fieldValueTransformer, configBuilderFactory, ","); - assertTrue(transformer.isMatching(Set.class, String.class)); - assertTrue(transformer.isMatching(Collection.class, String.class)); - assertFalse(transformer.isMatching(Object.class, String.class)); + assertThat(transformer.isMatching(Set.class, String.class)).isTrue(); + assertThat(transformer.isMatching(Collection.class, String.class)).isTrue(); + assertThat(transformer.isMatching(Object.class, String.class)).isFalse(); } private void initializeFactoryAndHelperMocks(){ - when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(genericsAndCastingHelper); - - when(genericsAndCastingHelper.castTypeToClass(List.class)).thenReturn((Class)List.class); - when(genericsAndCastingHelper.castTypeToClass(String.class)).thenReturn((Class)String.class); - when(genericsAndCastingHelper.castTypeToClass(Collection.class)).thenReturn((Class)Collection.class); - when(genericsAndCastingHelper.castTypeToClass(Object.class)).thenReturn((Class)Object.class); - when(genericsAndCastingHelper.castTypeToClass(((ParameterizedType)(transformer.getClass().getGenericSuperclass())).getActualTypeArguments()[0])).thenReturn((Class)Collection.class); + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringOrPrimitiveToPrimitiveTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringOrPrimitiveToPrimitiveTransformerTest.java new file mode 100644 index 00000000..24214678 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringOrPrimitiveToPrimitiveTransformerTest.java @@ -0,0 +1,92 @@ +package com.tngtech.configbuilder.annotation.typetransformer; + +import com.tngtech.configbuilder.configuration.ErrorMessageSetup; +import com.tngtech.configbuilder.exception.PrimitiveParsingException; +import com.tngtech.configbuilder.util.ConfigBuilderFactory; +import com.tngtech.configbuilder.util.FieldValueTransformer; +import com.tngtech.configbuilder.util.GenericsAndCastingHelper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class StringOrPrimitiveToPrimitiveTransformerTest { + + private final StringOrPrimitiveToPrimitiveTransformer stringOrPrimitiveToPrimitiveTransformer = new StringOrPrimitiveToPrimitiveTransformer(); + + @Mock + private FieldValueTransformer fieldValueTransformer; + @Mock + private ConfigBuilderFactory configBuilderFactory; + @Mock + private ErrorMessageSetup errorMessageSetup; + + @BeforeEach + void setupConfigBuilderFactoryMock() { + when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(errorMessageSetup); + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); + } + + @Test + public void testTransform() { + stringOrPrimitiveToPrimitiveTransformer.initialize(fieldValueTransformer, configBuilderFactory); + + stringOrPrimitiveToPrimitiveTransformer.setTargetType(boolean.class); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform("true")).isEqualTo(true); + + stringOrPrimitiveToPrimitiveTransformer.setTargetType(int.class); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform("1")).isEqualTo(1); + + stringOrPrimitiveToPrimitiveTransformer.setTargetType(double.class); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform("1")).isEqualTo(1.0); + + stringOrPrimitiveToPrimitiveTransformer.setTargetType(double.class); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform("1.0")).isEqualTo(1.0); + + stringOrPrimitiveToPrimitiveTransformer.setTargetType(boolean.class); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform(true)).isEqualTo(true); + + stringOrPrimitiveToPrimitiveTransformer.setTargetType(double.class); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform(1)).isEqualTo(1.0); + + stringOrPrimitiveToPrimitiveTransformer.setTargetType(double.class); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform(1.0)).isEqualTo(1.0); + } + + @Test + public void testThatSurroundingWhiteSpaceIsIgnored() { + stringOrPrimitiveToPrimitiveTransformer.initialize(fieldValueTransformer, configBuilderFactory); + + stringOrPrimitiveToPrimitiveTransformer.setTargetType(boolean.class); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform("true ")).isEqualTo(true); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform(" true ")).isEqualTo(true); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform(" true")).isEqualTo(true); + + stringOrPrimitiveToPrimitiveTransformer.setTargetType(int.class); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform("1 ")).isEqualTo(1); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform(" 1 ")).isEqualTo(1); + assertThat(stringOrPrimitiveToPrimitiveTransformer.transform(" 1")).isEqualTo(1); + } + + @Test + public void testTransformThrowsException() { + stringOrPrimitiveToPrimitiveTransformer.initialize(fieldValueTransformer, configBuilderFactory); + stringOrPrimitiveToPrimitiveTransformer.setTargetType(int.class); + assertThrows(PrimitiveParsingException.class, () -> stringOrPrimitiveToPrimitiveTransformer.transform(1.0)); + } + + @Test + public void testIsMatching() { + stringOrPrimitiveToPrimitiveTransformer.initialize(fieldValueTransformer, configBuilderFactory); + + assertThat(stringOrPrimitiveToPrimitiveTransformer.isMatching(int.class, Integer.class)).isTrue(); + assertThat(stringOrPrimitiveToPrimitiveTransformer.isMatching(String.class, int.class)).isTrue(); + assertThat(stringOrPrimitiveToPrimitiveTransformer.isMatching(int.class, Object.class)).isFalse(); + } +} diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToEnumTypeTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToEnumTypeTransformerTest.java new file mode 100644 index 00000000..384caf88 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToEnumTypeTransformerTest.java @@ -0,0 +1,55 @@ +package com.tngtech.configbuilder.annotation.typetransformer; + +import com.tngtech.configbuilder.configuration.ErrorMessageSetup; +import com.tngtech.configbuilder.util.ConfigBuilderFactory; +import com.tngtech.configbuilder.util.FieldValueTransformer; +import com.tngtech.configbuilder.util.GenericsAndCastingHelper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class StringToEnumTypeTransformerTest { + + private final StringToTestEnumTransformer transformer = new StringToTestEnumTransformer(); + + enum TestEnum { + ONE + } + + public static class StringToTestEnumTransformer extends StringToEnumTypeTransformer { + public StringToTestEnumTransformer() { + super(TestEnum.class); + } + } + + @Mock + private ConfigBuilderFactory configBuilderFactory; + + @BeforeEach + public void setUp() { + initializeFactoryMocks(); + transformer.initialize(new FieldValueTransformer(configBuilderFactory), configBuilderFactory); + } + + private void initializeFactoryMocks() { + when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(new ErrorMessageSetup()); + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); + } + + @Test + public void testTransform() { + assertThat(transformer.transform(TestEnum.ONE.name())).isSameAs(TestEnum.ONE); + } + + @Test + public void testIsMatching() { + assertThat(transformer.isMatching(String.class, TestEnum.class)).isTrue(); + assertThat(transformer.isMatching(String.class, Integer.class)).isFalse(); + } +} \ No newline at end of file diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToFileTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToFileTransformerTest.java new file mode 100644 index 00000000..050af000 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToFileTransformerTest.java @@ -0,0 +1,42 @@ +package com.tngtech.configbuilder.annotation.typetransformer; + +import com.tngtech.configbuilder.util.ConfigBuilderFactory; +import com.tngtech.configbuilder.util.FieldValueTransformer; +import com.tngtech.configbuilder.util.GenericsAndCastingHelper; +import java.io.File; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class StringToFileTransformerTest { + + private final StringToFileTransformer transformer = new StringToFileTransformer(); + + @Mock + private FieldValueTransformer fieldValueTransformer; + @Mock + private ConfigBuilderFactory configBuilderFactory; + + @Test + public void testTransform() { + assertThat(transformer.transform(".")).isEqualTo(new File(".")); + } + + @Test + public void testIsMatching() { + initializeFactoryAndHelperMocks(); + transformer.initialize(fieldValueTransformer, configBuilderFactory); + + assertThat(transformer.isMatching(String.class, File.class)).isTrue(); + assertThat(transformer.isMatching(Object.class, File.class)).isFalse(); + } + + private void initializeFactoryAndHelperMocks(){ + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); + } +} \ No newline at end of file diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToPathTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToPathTransformerTest.java index 22e798b4..f4d9db50 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToPathTransformerTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToPathTransformerTest.java @@ -3,58 +3,41 @@ import com.tngtech.configbuilder.util.ConfigBuilderFactory; import com.tngtech.configbuilder.util.FieldValueTransformer; import com.tngtech.configbuilder.util.GenericsAndCastingHelper; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -import java.lang.reflect.ParameterizedType; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collection; -import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class StringToPathTransformerTest { - private StringToPathTransformer transformer; + + private final StringToPathTransformer transformer = new StringToPathTransformer(); @Mock private FieldValueTransformer fieldValueTransformer; @Mock private ConfigBuilderFactory configBuilderFactory; - @Mock - private GenericsAndCastingHelper genericsAndCastingHelper; - - @Before - public void setUp() throws Exception { - transformer = new StringToPathTransformer(); - } @Test - public void testTransform() throws Exception { - assertEquals(Paths.get("/usr"), transformer.transform("/usr")); + public void testTransform() { + assertThat(transformer.transform("/usr")).isEqualTo(Paths.get("/usr")); } @Test - public void testIsMatching() throws Exception { + public void testIsMatching() { initializeFactoryAndHelperMocks(); transformer.initialize(fieldValueTransformer, configBuilderFactory); - assertTrue(transformer.isMatching(String.class, Path.class)); - assertFalse(transformer.isMatching(Object.class, Path.class)); + assertThat(transformer.isMatching(String.class, Path.class)).isTrue(); + assertThat(transformer.isMatching(Object.class, Path.class)).isFalse(); } private void initializeFactoryAndHelperMocks(){ - when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(genericsAndCastingHelper); - - when(genericsAndCastingHelper.castTypeToClass(String.class)).thenReturn((Class)String.class); - when(genericsAndCastingHelper.castTypeToClass(Object.class)).thenReturn((Class)Object.class); - when(genericsAndCastingHelper.castTypeToClass(((ParameterizedType)(transformer.getClass().getGenericSuperclass())).getActualTypeArguments()[1])).thenReturn((Class)Path.class); + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToPrimitiveTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToPrimitiveTransformerTest.java deleted file mode 100644 index a707811c..00000000 --- a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/StringToPrimitiveTransformerTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.tngtech.configbuilder.annotation.typetransformer; - -import com.tngtech.configbuilder.configuration.ErrorMessageSetup; -import com.tngtech.configbuilder.exception.PrimitiveParsingException; -import com.tngtech.configbuilder.util.ConfigBuilderFactory; -import com.tngtech.configbuilder.util.FieldValueTransformer; -import com.tngtech.configbuilder.util.GenericsAndCastingHelper; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class StringToPrimitiveTransformerTest { - - private StringOrPrimitiveToPrimitiveTransformer stringOrPrimitiveToPrimitiveTransformer; - - @Mock - private FieldValueTransformer fieldValueTransformer; - @Mock - private ConfigBuilderFactory configBuilderFactory; - @Mock - private ErrorMessageSetup errorMessageSetup; - @Mock - private GenericsAndCastingHelper genericsAndCastingHelper; - - @Before - public void setUp() throws Exception { - stringOrPrimitiveToPrimitiveTransformer = new StringOrPrimitiveToPrimitiveTransformer(); - } - - @Test - public void testTransform() throws Exception { - initializeFactoryAndHelperMocks(); - stringOrPrimitiveToPrimitiveTransformer.initialize(fieldValueTransformer, configBuilderFactory); - - stringOrPrimitiveToPrimitiveTransformer.setTargetType(boolean.class); - assertEquals(true, stringOrPrimitiveToPrimitiveTransformer.transform("true")); - - stringOrPrimitiveToPrimitiveTransformer.setTargetType(int.class); - assertEquals(1, stringOrPrimitiveToPrimitiveTransformer.transform("1")); - - stringOrPrimitiveToPrimitiveTransformer.setTargetType(double.class); - assertEquals(1.0, stringOrPrimitiveToPrimitiveTransformer.transform("1")); - - stringOrPrimitiveToPrimitiveTransformer.setTargetType(double.class); - assertEquals(1.0, stringOrPrimitiveToPrimitiveTransformer.transform("1.0")); - - stringOrPrimitiveToPrimitiveTransformer.setTargetType(boolean.class); - assertEquals(true, stringOrPrimitiveToPrimitiveTransformer.transform(true)); - - stringOrPrimitiveToPrimitiveTransformer.setTargetType(double.class); - assertEquals(1.0, stringOrPrimitiveToPrimitiveTransformer.transform(1)); - - stringOrPrimitiveToPrimitiveTransformer.setTargetType(double.class); - assertEquals(1.0, stringOrPrimitiveToPrimitiveTransformer.transform(1.0)); - } - - @Test(expected = PrimitiveParsingException.class) - public void testTransformThrowsException() throws Exception { - initializeFactoryAndHelperMocks(); - stringOrPrimitiveToPrimitiveTransformer.initialize(fieldValueTransformer, configBuilderFactory); - stringOrPrimitiveToPrimitiveTransformer.setTargetType(int.class); - assertEquals(1, stringOrPrimitiveToPrimitiveTransformer.transform(1.0)); - } - - @Test - public void testIsMatching() throws Exception { - initializeFactoryAndHelperMocks(); - - stringOrPrimitiveToPrimitiveTransformer.initialize(fieldValueTransformer, configBuilderFactory); - - assertTrue(stringOrPrimitiveToPrimitiveTransformer.isMatching(int.class, Integer.class)); - assertTrue(stringOrPrimitiveToPrimitiveTransformer.isMatching(String.class, int.class)); - assertFalse(stringOrPrimitiveToPrimitiveTransformer.isMatching(int.class, Object.class)); - } - - private void initializeFactoryAndHelperMocks() { - when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(errorMessageSetup); - when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(genericsAndCastingHelper); - - when(genericsAndCastingHelper.castTypeToClass(boolean.class)).thenReturn((Class)boolean.class); - when(genericsAndCastingHelper.castTypeToClass(double.class)).thenReturn((Class)double.class); - when(genericsAndCastingHelper.castTypeToClass(int.class)).thenReturn((Class)int.class); - when(genericsAndCastingHelper.isPrimitiveOrWrapper(int.class)).thenReturn(true); - when(genericsAndCastingHelper.isPrimitiveOrWrapper(Integer.class)).thenReturn(true); - when(genericsAndCastingHelper.isPrimitiveOrWrapper(Object.class)).thenReturn(false); - } -} diff --git a/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformerTest.java b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformerTest.java new file mode 100644 index 00000000..d620a60e --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/annotation/typetransformer/TypeTransformerTest.java @@ -0,0 +1,120 @@ +package com.tngtech.configbuilder.annotation.typetransformer; + +import com.tngtech.configbuilder.util.ConfigBuilderFactory; +import com.tngtech.configbuilder.util.FieldValueTransformer; +import com.tngtech.configbuilder.util.GenericsAndCastingHelper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class TypeTransformerTest { + + private static class TestTypeTransformer extends TypeTransformer { + @Override + public Integer transform(String argument) { + return Integer.getInteger(argument); + } + } + + private static class TestIntermediateTypeTransformer extends TypeTransformer { + @Override + public T transform(String argument) { + return null; + } + } + + private static class TestInheritedTypeTransformer extends TestIntermediateTypeTransformer { + } + + private static class TestUntypedIntermediateTypeTransformer extends TypeTransformer { + @Override + public V transform(U argument) { + return null; + } + } + + private static class TestUntypedInheritedTypeTransformer extends TestUntypedIntermediateTypeTransformer { + } + + private static class TestUntypedRevertedIntermediateTypeTransformer extends TypeTransformer { + @Override + public V transform(U argument) { + return null; + } + } + + private static class TestUntypedRevertedInheritedTypeTransformer extends TestUntypedRevertedIntermediateTypeTransformer { + } + + @Mock + private FieldValueTransformer fieldValueTransformer; + @Mock + private ConfigBuilderFactory configBuilderFactory; + + @BeforeEach + public void setUp() { + when(configBuilderFactory.getInstance(GenericsAndCastingHelper.class)).thenReturn(new GenericsAndCastingHelper()); + } + + private TypeTransformer createSimpleTypeTransformer() { + TypeTransformer typeTransformer = new TestTypeTransformer(); + typeTransformer.initialize(fieldValueTransformer, configBuilderFactory); + return typeTransformer; + } + + private TypeTransformer createInheritedTypeTransformer() { + TypeTransformer typeTransformer = new TestInheritedTypeTransformer(); + typeTransformer.initialize(fieldValueTransformer, configBuilderFactory); + return typeTransformer; + } + + private TypeTransformer createUntypedInheritedTypeTransformer() { + TypeTransformer typeTransformer = new TestUntypedInheritedTypeTransformer(); + typeTransformer.initialize(fieldValueTransformer, configBuilderFactory); + return typeTransformer; + } + + private TypeTransformer createUntypedRevertedInheritedTypeTransformer() { + TypeTransformer typeTransformer = new TestUntypedRevertedInheritedTypeTransformer(); + typeTransformer.initialize(fieldValueTransformer, configBuilderFactory); + return typeTransformer; + } + + @Test + public void testSimpleTypeTransformer() { + final TypeTransformer typeTransformer = createSimpleTypeTransformer(); + + //noinspection unchecked + assertThat(typeTransformer.isMatching(String.class, Integer.class)).isTrue(); + } + + @Test + public void testInheritedTypeTransformerMatch() { + final TypeTransformer typeTransformer = createInheritedTypeTransformer(); + + //noinspection unchecked + assertThat(typeTransformer.isMatching(String.class, Integer.class)).isTrue(); + } + + @Test + public void testUntypedInheritedTypeTransformerMatch() { + final TypeTransformer typeTransformer = createUntypedInheritedTypeTransformer(); + + //noinspection unchecked + assertThat(typeTransformer.isMatching(String.class, Integer.class)).isTrue(); + } + + @Test + public void testUntypedRevertedInheritedTypeTransformerMatch() { + final TypeTransformer typeTransformer = createUntypedRevertedInheritedTypeTransformer(); + + //noinspection uncheckedy + assertThat(typeTransformer.isMatching(String.class, Integer.class)).isTrue(); + } +} \ No newline at end of file diff --git a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueProcessorTest.java index 93d15d32..9b8e3f71 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/CommandLineValueProcessorTest.java @@ -3,19 +3,19 @@ import com.tngtech.configbuilder.configuration.BuilderConfiguration; import com.tngtech.configbuilder.util.ConfigBuilderFactory; import org.apache.commons.cli.CommandLine; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class CommandLineValueProcessorTest { - private CommandLineValueProcessor commandLineValueProcessor; + private final CommandLineValueProcessor commandLineValueProcessor = new CommandLineValueProcessor(); @Mock private BuilderConfiguration builderConfiguration; @@ -23,29 +23,60 @@ public class CommandLineValueProcessorTest { private ConfigBuilderFactory configBuilderFactory; @Mock private CommandLine commandLine; - @Mock - private CommandLineValue commandLineValue; - @Before - public void setUp() throws Exception { - commandLineValueProcessor = new CommandLineValueProcessor(); + @BeforeEach + public void setUpMocks() { + when(configBuilderFactory.getInstance(BuilderConfiguration.class)).thenReturn(builderConfiguration); + when(builderConfiguration.getCommandLine()).thenReturn(commandLine); } @Test - public void testCommandLineValueProcessor() { - when(configBuilderFactory.getInstance(BuilderConfiguration.class)).thenReturn(builderConfiguration); - when(builderConfiguration.getCommandLine()).thenReturn(commandLine); - when(commandLineValue.shortOpt()).thenReturn("value"); - assertEquals("false", commandLineValueProcessor.getValue(commandLineValue, configBuilderFactory).toString()); + public void testCommandLineValueProcessorOptionNotPresent() { + CommandLineValue commandLineValue = TestConfig.getAnnotation("value"); + assertThat(commandLineValueProcessor.getValue(commandLineValue, configBuilderFactory)).isEqualTo("false"); } @Test - public void testCommandLineValueProcessorWithArg() { - when(configBuilderFactory.getInstance(BuilderConfiguration.class)).thenReturn(builderConfiguration); - when(builderConfiguration.getCommandLine()).thenReturn(commandLine); - when(commandLineValue.shortOpt()).thenReturn("value"); - when(commandLineValue.hasArg()).thenReturn(true); + public void testCommandLineValueProcessorShortOptionPresent() { + CommandLineValue commandLineValue = TestConfig.getAnnotation("value"); + when(commandLine.hasOption("value")).thenReturn(true); + assertThat(commandLineValueProcessor.getValue(commandLineValue, configBuilderFactory)).isEqualTo("true"); + } + + @Test + public void testCommandLineValueProcessorLongOptionPresent() { + CommandLineValue commandLineValue = TestConfig.getAnnotation("value"); + when(commandLine.hasOption("value")).thenReturn(false); + when(commandLine.hasOption("longOption")).thenReturn(true); + assertThat(commandLineValueProcessor.getValue(commandLineValue, configBuilderFactory)).isEqualTo("true"); + } + + @Test + public void testCommandLineValueProcessorWithArgValueOptionNotPresent() { + CommandLineValue commandLineValue = TestConfig.getAnnotation("valueWithArg"); + assertThat(commandLineValueProcessor.getValue(commandLineValue, configBuilderFactory)).isNull(); + } + + @Test + public void testCommandLineValueProcessorWithArg() { + CommandLineValue commandLineValue = TestConfig.getAnnotation("valueWithArg"); when(commandLine.getOptionValue("value")).thenReturn("passed"); - assertEquals("passed", commandLineValueProcessor.getValue(commandLineValue, configBuilderFactory)); + assertThat(commandLineValueProcessor.getValue(commandLineValue, configBuilderFactory)).isEqualTo("passed"); + } + + @SuppressWarnings("unused") + static class TestConfig { + @CommandLineValue(shortOpt = "value", longOpt = "longOption") + String value; + @CommandLineValue(shortOpt = "value", longOpt = "longOption", hasArg = true) + String valueWithArg; + + static CommandLineValue getAnnotation(String fieldName) { + try { + return TestConfig.class.getDeclaredField(fieldName).getAnnotation(CommandLineValue.class); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/DefaultValueProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/DefaultValueProcessorTest.java index 2ddff90e..7b5d4193 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/DefaultValueProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/DefaultValueProcessorTest.java @@ -1,16 +1,16 @@ package com.tngtech.configbuilder.annotation.valueextractor; import com.tngtech.configbuilder.util.ConfigBuilderFactory; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class DefaultValueProcessorTest { private DefaultValueProcessor defaultValueProcessor; @@ -20,15 +20,14 @@ public class DefaultValueProcessorTest { @Mock private DefaultValue defaultValue; - @Before - public void setUp() throws Exception { + @BeforeEach + public void setUp() { defaultValueProcessor = new DefaultValueProcessor(); } @Test public void testDefaultValueProcessor() { - when(defaultValue.value()).thenReturn("value"); - assertEquals("value", defaultValueProcessor.getValue(defaultValue, configBuilderFactory).toString()); + assertThat(defaultValueProcessor.getValue(defaultValue, configBuilderFactory)).isEqualTo("value"); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/EnvironmentVariableProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/EnvironmentVariableProcessorTest.java index a24f2ae3..d76dda74 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/EnvironmentVariableProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/EnvironmentVariableProcessorTest.java @@ -1,37 +1,28 @@ package com.tngtech.configbuilder.annotation.valueextractor; -import com.tngtech.configbuilder.configuration.BuilderConfiguration; import com.tngtech.configbuilder.util.ConfigBuilderFactory; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class EnvironmentVariableProcessorTest { - private EnvironmentVariableProcessor environmentVariableProcessor; + private final EnvironmentVariableProcessor environmentVariableProcessor = new EnvironmentVariableProcessor(); @Mock private EnvironmentVariableValue environmentVariableValue; - @Mock - private BuilderConfiguration builderConfiguration; @Mock private ConfigBuilderFactory configBuilderFactory; - - @Before - public void setUp() throws Exception { - environmentVariableProcessor = new EnvironmentVariableProcessor(); - when(environmentVariableValue.value()).thenReturn("PATH"); - } @Test - public void testGetValue() throws Exception { - assertEquals(System.getenv("PATH"), environmentVariableProcessor.getValue(environmentVariableValue, configBuilderFactory)); + public void testGetValue() { + when(environmentVariableValue.value()).thenReturn("PATH"); + assertThat(environmentVariableProcessor.getValue(environmentVariableValue, configBuilderFactory)).isEqualTo(System.getenv("PATH")); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/ImportedValueProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/ImportedValueProcessorTest.java index ebd76c36..9a86c4f6 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/ImportedValueProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/ImportedValueProcessorTest.java @@ -4,17 +4,17 @@ import com.tngtech.configbuilder.configuration.ErrorMessageSetup; import com.tngtech.configbuilder.exception.ImportedConfigurationException; import com.tngtech.configbuilder.util.ConfigBuilderFactory; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class ImportedValueProcessorTest { private class ImportedTestConfig { @@ -35,7 +35,7 @@ private class ImportedTestConfig { private ImportedValueProcessor importedValueProcessor; - @Before + @BeforeEach public void setUp() { importedValueProcessor = new ImportedValueProcessor(); importedTestConfig = new ImportedTestConfig(); @@ -46,9 +46,9 @@ public void testGetIntegerValue() { when(configBuilderFactory.getInstance(BuilderConfiguration.class)).thenReturn(builderConfiguration); when(builderConfiguration.getImportedConfiguration()).thenReturn(importedTestConfig); when(importedValue.value()).thenReturn("intField"); - - int actualResult = (int) importedValueProcessor.getValue(importedValue, configBuilderFactory); - assertThat(actualResult, equalTo(23)); + + int actualResult = (Integer) importedValueProcessor.getValue(importedValue, configBuilderFactory); + assertThat(actualResult).isEqualTo(23); } @Test @@ -58,7 +58,7 @@ public void testGetStringValue() { when(importedValue.value()).thenReturn("stringField"); String actualResult = (String) importedValueProcessor.getValue(importedValue, configBuilderFactory); - assertThat(actualResult, equalTo("Foo")); + assertThat(actualResult).isEqualTo("Foo"); } @Test @@ -67,18 +67,18 @@ public void testReturnNullIfNoImportedConfigurationProvided() { when(builderConfiguration.getImportedConfiguration()).thenReturn(null); String actualResult = (String) importedValueProcessor.getValue(importedValue, configBuilderFactory); - assertThat(actualResult, equalTo(null)); + assertThat(actualResult).isNull(); } - @Test(expected = ImportedConfigurationException.class) + @Test public void testExceptionIfFieldNotPresent() { when(configBuilderFactory.getInstance(BuilderConfiguration.class)).thenReturn(builderConfiguration); when(builderConfiguration.getImportedConfiguration()).thenReturn(importedTestConfig); when(importedValue.value()).thenReturn("notAField"); when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(errorMessageSetup); - when(errorMessageSetup.getErrorMessage(ImportedConfigurationException.class)).thenReturn("Just a message"); + when(errorMessageSetup.getErrorMessage(ImportedConfigurationException.class, "notAField")).thenReturn("Just a message"); - importedValueProcessor.getValue(importedValue, configBuilderFactory); + assertThrows(ImportedConfigurationException.class, () -> importedValueProcessor.getValue(importedValue, configBuilderFactory)); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/PropertyValueProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/PropertyValueProcessorTest.java index 06b18ffb..a4c72e6e 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/PropertyValueProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/PropertyValueProcessorTest.java @@ -2,21 +2,26 @@ import com.tngtech.configbuilder.configuration.BuilderConfiguration; import com.tngtech.configbuilder.util.ConfigBuilderFactory; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - import java.util.Properties; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class PropertyValueProcessorTest { - private PropertyValueProcessor propertyValueProcessor; + private final PropertyValueProcessor propertyValueProcessor = new PropertyValueProcessor(); @Mock private BuilderConfiguration builderConfiguration; @@ -27,17 +32,59 @@ public class PropertyValueProcessorTest { @Mock PropertyValue propertyValue; - @Before - public void setUp() throws Exception { - propertyValueProcessor = new PropertyValueProcessor(); + @BeforeEach + public void setUp() { + when(configBuilderFactory.getInstance(BuilderConfiguration.class)).thenReturn(builderConfiguration); + when(builderConfiguration.getProperties()).thenReturn(properties); } @Test public void testPropertyValueProcessor() { - when(configBuilderFactory.getInstance(BuilderConfiguration.class)).thenReturn(builderConfiguration); - when(builderConfiguration.getProperties()).thenReturn(properties); + when(builderConfiguration.getPropertyNamePrefixes()).thenReturn(new String[]{""}); when(propertyValue.value()).thenReturn("test"); + when(properties.containsKey("test")).thenReturn(true); when(properties.getProperty("test")).thenReturn("passed"); - assertEquals("passed", propertyValueProcessor.getValue(propertyValue, configBuilderFactory)); + + assertThat(propertyValueProcessor.getValue(propertyValue, configBuilderFactory)).isEqualTo("passed"); + } + + @Test + public void testPropertyValueProcessorWithPropertyNamePrefix() { + when(builderConfiguration.getPropertyNamePrefixes()).thenReturn(new String[]{"prefix."}); + when(propertyValue.value()).thenReturn("test"); + when(properties.containsKey("prefix.test")).thenReturn(true); + when(properties.getProperty("prefix.test")).thenReturn("passed"); + + assertThat(propertyValueProcessor.getValue(propertyValue, configBuilderFactory)).isEqualTo("passed"); + } + + @Test + public void testPropertyValueProcessorWithPropertyNamePrefixes() { + when(builderConfiguration.getPropertyNamePrefixes()).thenReturn(new String[]{"other.", "prefix."}); + when(propertyValue.value()).thenReturn("test"); + when(properties.containsKey("other.test")).thenReturn(false); + when(properties.containsKey("prefix.test")).thenReturn(true); + when(properties.getProperty("prefix.test")).thenReturn("passed"); + + assertThat(propertyValueProcessor.getValue(propertyValue, configBuilderFactory)).isEqualTo("passed"); + + final InOrder inOrder = inOrder(properties); + inOrder.verify(properties).containsKey("other.test"); + inOrder.verify(properties).containsKey("prefix.test"); + inOrder.verify(properties).getProperty("prefix.test"); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testPropertyValueProcessorPropertyNotFound() { + when(builderConfiguration.getPropertyNamePrefixes()).thenReturn(new String[]{""}); + when(propertyValue.value()).thenReturn("test"); + when(properties.containsKey("test")).thenReturn(false); + + assertThat(propertyValueProcessor.getValue(propertyValue, configBuilderFactory)).isNull(); + + verify(properties).containsKey("test"); + verify(properties, never()).getProperty(anyString()); + verifyNoMoreInteractions(properties); } } diff --git a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyProcessorTest.java b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyProcessorTest.java index 5325b9bc..acf08228 100644 --- a/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyProcessorTest.java +++ b/src/test/java/com/tngtech/configbuilder/annotation/valueextractor/SystemPropertyProcessorTest.java @@ -1,34 +1,27 @@ package com.tngtech.configbuilder.annotation.valueextractor; import com.tngtech.configbuilder.util.ConfigBuilderFactory; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class SystemPropertyProcessorTest { - private SystemPropertyProcessor systemPropertyProcessor; + private final SystemPropertyProcessor systemPropertyProcessor = new SystemPropertyProcessor(); @Mock private SystemPropertyValue systemPropertyValue; - @Mock private ConfigBuilderFactory configBuilderFactory; - @Before - public void setUp() throws Exception { - systemPropertyProcessor = new SystemPropertyProcessor(); - when(systemPropertyValue.value()).thenReturn("user.language"); - } - @Test - public void testGetValue() throws Exception { - assertEquals(System.getProperty("user.language"), systemPropertyProcessor.getValue(systemPropertyValue, configBuilderFactory).toString()); + public void testGetValue() { + when(systemPropertyValue.value()).thenReturn("user.language"); + assertThat(systemPropertyProcessor.getValue(systemPropertyValue, configBuilderFactory)).isEqualTo(System.getProperty("user.language")); } } diff --git a/src/test/java/com/tngtech/configbuilder/configuration/BuilderConfigurationTest.java b/src/test/java/com/tngtech/configbuilder/configuration/BuilderConfigurationTest.java index ee462438..e48c94ec 100644 --- a/src/test/java/com/tngtech/configbuilder/configuration/BuilderConfigurationTest.java +++ b/src/test/java/com/tngtech/configbuilder/configuration/BuilderConfigurationTest.java @@ -1,49 +1,21 @@ package com.tngtech.configbuilder.configuration; -import org.junit.Before; -import org.junit.Test; - import java.util.Properties; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; - +import static org.assertj.core.api.Assertions.assertThat; public class BuilderConfigurationTest { - private BuilderConfiguration builderConfiguration; - - @Before - public void setUp() throws Exception { - builderConfiguration = new BuilderConfiguration(); - } - - @Test - public void testGetCommandLineArgs() throws Exception { - assertEquals(null, builderConfiguration.getCommandLine()); - } - - @Test - public void testGetProperties() throws Exception { - assertEquals(new Properties(), builderConfiguration.getProperties()); - } - - @Test - public void testSetProperties() throws Exception { - - } - - @Test - public void testSetCommandLineArgs() throws Exception { - - } + private final BuilderConfiguration builderConfiguration = new BuilderConfiguration(); @Test - public void testSetAnnotationOrder() throws Exception { - + public void testGetCommandLineArgs() { + assertThat(builderConfiguration.getCommandLine()).isNull(); } @Test - public void testGetAnnotationOrder() throws Exception { - + public void testGetProperties() { + assertThat(builderConfiguration.getProperties()).isEqualTo(new Properties()); } } diff --git a/src/test/java/com/tngtech/configbuilder/configuration/ErrorMessageSetupTest.java b/src/test/java/com/tngtech/configbuilder/configuration/ErrorMessageSetupTest.java index ff6a2fd4..bd1666a8 100644 --- a/src/test/java/com/tngtech/configbuilder/configuration/ErrorMessageSetupTest.java +++ b/src/test/java/com/tngtech/configbuilder/configuration/ErrorMessageSetupTest.java @@ -1,69 +1,63 @@ package com.tngtech.configbuilder.configuration; import com.tngtech.propertyloader.PropertyLoader; -import org.apache.commons.cli.ParseException; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; - import java.util.Locale; import java.util.Properties; +import org.apache.commons.cli.ParseException; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class ErrorMessageSetupTest { @Mock private PropertyLoader propertyLoader; - private ErrorMessageSetup errorMessageSetup; - - @Before - public void setUp() throws Exception { - errorMessageSetup = new ErrorMessageSetup(); - when(propertyLoader.withExtension("properties")).thenReturn(propertyLoader); - when(propertyLoader.load("errors")).thenReturn(new Properties()); - } + private final ErrorMessageSetup errorMessageSetup = new ErrorMessageSetup(); @Test - public void testInitializeDE() throws Exception { + public void testInitializeDE() { + when(propertyLoader.load("errors")).thenReturn(new Properties()); Locale.setDefault(Locale.GERMAN); errorMessageSetup.initialize("errors", propertyLoader); - assertEquals("Command Line Argumente konnten nicht verarbeitet werden.", errorMessageSetup.getErrorMessage(ParseException.class)); + assertThat(errorMessageSetup.getErrorMessage(ParseException.class)).isEqualTo("Command Line Argumente konnten nicht verarbeitet werden."); } @Test - public void testInitializeEN() throws Exception { + public void testInitializeEN() { + when(propertyLoader.load("errors")).thenReturn(new Properties()); Locale.setDefault(Locale.ENGLISH); errorMessageSetup.initialize("errors", propertyLoader); - assertEquals("unable to parse command line arguments", errorMessageSetup.getErrorMessage(ParseException.class)); + assertThat(errorMessageSetup.getErrorMessage(ParseException.class)).isEqualTo("unable to parse command line arguments"); } @Test - public void testInitializeOther() throws Exception { + public void testInitializeOther() { + when(propertyLoader.load("errors")).thenReturn(new Properties()); Locale.setDefault(Locale.ITALIAN); errorMessageSetup.initialize("errors", propertyLoader); - assertEquals("unable to parse command line arguments", errorMessageSetup.getErrorMessage(ParseException.class)); + assertThat(errorMessageSetup.getErrorMessage(ParseException.class)).isEqualTo("unable to parse command line arguments"); } @Test - public void testGetErrorMessageForExceptionInstance() throws Exception { + public void testGetErrorMessageForExceptionInstance() { Locale.setDefault(Locale.ENGLISH); errorMessageSetup.initialize(null, propertyLoader); ParseException parseException = new ParseException("message"); - assertEquals("unable to parse command line arguments", errorMessageSetup.getErrorMessage(parseException)); + assertThat(errorMessageSetup.getErrorMessage(parseException)).isEqualTo("unable to parse command line arguments"); } @Test - public void testGetErrorMessageForUnknownException() throws Exception { + public void testGetErrorMessageForUnknownException() { Locale.setDefault(Locale.ENGLISH); errorMessageSetup.initialize(null, propertyLoader); RuntimeException runtimeException = new RuntimeException(); - assertEquals("java.lang.RuntimeException was thrown", errorMessageSetup.getErrorMessage(runtimeException)); - assertEquals("java.lang.RuntimeException was thrown", errorMessageSetup.getErrorMessage(RuntimeException.class)); + assertThat(errorMessageSetup.getErrorMessage(runtimeException)).isEqualTo("java.lang.RuntimeException was thrown"); + assertThat(errorMessageSetup.getErrorMessage(RuntimeException.class)).isEqualTo("java.lang.RuntimeException was thrown"); } } diff --git a/src/test/java/com/tngtech/configbuilder/testclasses/ExtendedTestConfig.java b/src/test/java/com/tngtech/configbuilder/testclasses/ExtendedTestConfig.java new file mode 100644 index 00000000..20bda33e --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/testclasses/ExtendedTestConfig.java @@ -0,0 +1,27 @@ +package com.tngtech.configbuilder.testclasses; + +import com.tngtech.configbuilder.annotation.valueextractor.DefaultValue; +import com.tngtech.configbuilder.annotation.valueextractor.ImportedValue; + +public class ExtendedTestConfig extends TestConfig { + @DefaultValue("4") + @ImportedValue("additionalNumber") + private int additionalNumber; + + @DefaultValue("5") + @ImportedValue("someNumber") + private int someNumber; + + public int getAdditionalNumber() { + return additionalNumber; + } + + @Override + public Integer getSomeNumber() { + return someNumber; + } + + public Integer getSuperSomeNumber() { + return super.getSomeNumber(); + } +} diff --git a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfig.java b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfig.java index 4226e47c..8a453d09 100644 --- a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfig.java +++ b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfig.java @@ -4,27 +4,36 @@ import com.tngtech.configbuilder.annotation.configuration.Separator; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertiesFiles; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyExtension; +import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyFilters; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyLocations; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertySuffixes; -import com.tngtech.configbuilder.annotation.typetransformer.*; +import com.tngtech.configbuilder.annotation.typetransformer.CharacterSeparatedStringToStringListTransformer; +import com.tngtech.configbuilder.annotation.typetransformer.TypeTransformer; +import com.tngtech.configbuilder.annotation.typetransformer.TypeTransformers; import com.tngtech.configbuilder.annotation.validation.Validation; -import com.tngtech.configbuilder.annotation.valueextractor.*; +import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; +import com.tngtech.configbuilder.annotation.valueextractor.DefaultValue; +import com.tngtech.configbuilder.annotation.valueextractor.EnvironmentVariableValue; +import com.tngtech.configbuilder.annotation.valueextractor.ImportedValue; +import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; +import com.tngtech.configbuilder.annotation.valueextractor.SystemPropertyValue; import com.tngtech.propertyloader.PropertyLoader; - +import com.tngtech.propertyloader.impl.filters.DecryptingFilter; +import com.tngtech.propertyloader.impl.filters.VariableResolvingFilter; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; @PropertyExtension("testproperties") @PropertySuffixes(extraSuffixes = {"test"}) @PropertyLocations(resourcesForClasses = {PropertyLoader.class}) +@PropertyFilters({VariableResolvingFilter.class, DecryptingFilter.class}) @PropertiesFiles("demoapp-configuration") public class TestConfig { - public TestConfig() { - - } - - public class TestConfigFactory extends TypeTransformer { + public static class TestConfigFactory extends TypeTransformer { public TestConfig transform(String input) { TestConfig testConfig = new TestConfig(); testConfig.setSomeString(input); @@ -52,7 +61,7 @@ public TestConfig transform(String input) { @DefaultValue("/etc,/usr") @ImportedValue("stringCollection") - private HashSet pathCollection; + private Set pathCollection; @ImportedValue("stringCollection") private Iterable copiedStringCollection; @@ -73,6 +82,10 @@ public TestConfig transform(String input) { public void setSomeNumber(Integer someNumber) { this.someNumber = someNumber; } + + public Integer getSomeNumber() { + return someNumber; + } public void setSomeString(String someString) { this.someString = someString; @@ -98,7 +111,7 @@ public Collection getPathCollection() { return pathCollection; } - public void setPathCollection(HashSet pathCollection) { + public void setPathCollection(Set pathCollection) { this.pathCollection = pathCollection; } @@ -123,7 +136,7 @@ public void setCopiedStringCollection(Iterable copiedStringCollection) { } @Validation - private void validate() { + protected void validate() { System.out.println("config validated"); } } diff --git a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigNotNullViolation.java b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigNotNullViolation.java deleted file mode 100644 index c4fd6cb9..00000000 --- a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigNotNullViolation.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.tngtech.configbuilder.testclasses; - -import com.tngtech.configbuilder.annotation.configuration.LoadingOrder; -import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertiesFiles; -import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyExtension; -import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyLocations; -import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertySuffixes; -import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; -import com.tngtech.configbuilder.annotation.valueextractor.DefaultValue; -import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; -import com.tngtech.propertyloader.PropertyLoader; - -import javax.validation.constraints.NotNull; - -@PropertyExtension("testproperties") -@PropertySuffixes(extraSuffixes = {"test"}) -@PropertyLocations(resourcesForClasses = {PropertyLoader.class}, fromClassLoader = true) -@PropertiesFiles("demoapp-configuration") -@LoadingOrder(value = {CommandLineValue.class, PropertyValue.class, DefaultValue.class}) -public class TestConfigNotNullViolation { - - @NotNull - @DefaultValue("keyThatDoesNotExist") - private String string; -} diff --git a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigPropertyNamePrefix.java b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigPropertyNamePrefix.java new file mode 100644 index 00000000..e24794e1 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigPropertyNamePrefix.java @@ -0,0 +1,17 @@ +package com.tngtech.configbuilder.testclasses; + +import com.tngtech.configbuilder.annotation.configuration.PropertyNamePrefix; +import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertiesFiles; +import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; + +@PropertiesFiles("testPropertyNamePrefix") +@PropertyNamePrefix("test.prefix.") +public class TestConfigPropertyNamePrefix { + + @PropertyValue("foo") + private String foo; + + public String getFoo() { + return foo; + } +} diff --git a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigThrowsInvocationTargetExceptionException.java b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigThrowsInvocationTargetExceptionException.java index 5d03ff05..4cce0a33 100644 --- a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigThrowsInvocationTargetExceptionException.java +++ b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigThrowsInvocationTargetExceptionException.java @@ -5,16 +5,13 @@ import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyExtension; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyLocations; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertySuffixes; -import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; -import com.tngtech.configbuilder.annotation.valueextractor.DefaultValue; -import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; import com.tngtech.propertyloader.PropertyLoader; @PropertyExtension("testproperties") @PropertySuffixes(extraSuffixes = {"test"}) @PropertyLocations(resourcesForClasses = {PropertyLoader.class}) @PropertiesFiles("demoapp-configuration") -@LoadingOrder(value = {CommandLineValue.class, PropertyValue.class, DefaultValue.class}) +@LoadingOrder() public class TestConfigThrowsInvocationTargetExceptionException { private int number; diff --git a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigThrowsPrimitiveParsingException.java b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigThrowsPrimitiveParsingException.java index 79593a10..67e2907a 100644 --- a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigThrowsPrimitiveParsingException.java +++ b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigThrowsPrimitiveParsingException.java @@ -1,14 +1,6 @@ package com.tngtech.configbuilder.testclasses; -import com.tngtech.configbuilder.annotation.configuration.LoadingOrder; -import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertiesFiles; -import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyExtension; -import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyLocations; -import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertySuffixes; -import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; import com.tngtech.configbuilder.annotation.valueextractor.DefaultValue; -import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; -import com.tngtech.propertyloader.PropertyLoader; public class TestConfigThrowsPrimitiveParsingException { diff --git a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigWithoutAnnotations.java b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigWithoutAnnotations.java new file mode 100644 index 00000000..1d321cfa --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigWithoutAnnotations.java @@ -0,0 +1,141 @@ +package com.tngtech.configbuilder.testclasses; + +import com.tngtech.configbuilder.annotation.configuration.LoadingOrder; +import com.tngtech.configbuilder.annotation.configuration.Separator; +import com.tngtech.configbuilder.annotation.typetransformer.CharacterSeparatedStringToStringListTransformer; +import com.tngtech.configbuilder.annotation.typetransformer.TypeTransformer; +import com.tngtech.configbuilder.annotation.typetransformer.TypeTransformers; +import com.tngtech.configbuilder.annotation.validation.Validation; +import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; +import com.tngtech.configbuilder.annotation.valueextractor.DefaultValue; +import com.tngtech.configbuilder.annotation.valueextractor.EnvironmentVariableValue; +import com.tngtech.configbuilder.annotation.valueextractor.ImportedValue; +import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; +import com.tngtech.configbuilder.annotation.valueextractor.SystemPropertyValue; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +public class TestConfigWithoutAnnotations { + + public class TestConfigFactory extends TypeTransformer { + public TestConfigWithoutAnnotations transform(String input) { + TestConfigWithoutAnnotations testConfig = new TestConfigWithoutAnnotations(); + testConfig.setSomeString(input); + return testConfig; + } + } + + @DefaultValue("3") + @ImportedValue("someNumber") + private int someNumber; + + @PropertyValue("keyThatDoesNotExist") + private int shouldBeZero; + + @PropertyValue("a") + private String someString; + + @PropertyValue("b") + private String otherString; + + @CommandLineValue(shortOpt = "u", longOpt = "user") + private boolean aBoolean; + + @LoadingOrder(value = {CommandLineValue.class}) + @CommandLineValue(shortOpt = "c", longOpt = "collection", hasArg = true, description = "command line option description") + @TypeTransformers({CharacterSeparatedStringToStringListTransformer.class}) + private Collection stringCollection; + + @DefaultValue("/etc,/usr") + @ImportedValue("stringCollection") + private HashSet pathCollection; + + @ImportedValue("stringCollection") + private Iterable copiedStringCollection; + + @Separator(";") + @DefaultValue("1;2;3;4;5") + private List integerList; + + @TypeTransformers(TestConfigFactory.class) + private ArrayList testConfigList; + + @EnvironmentVariableValue("HOME") + private Path homeDir; + + @SystemPropertyValue("user.language") + private String systemProperty; + + public void setSomeNumber(Integer someNumber) { + this.someNumber = someNumber; + } + + public Integer getSomeNumber() { + return someNumber; + } + + public void setSomeString(String someString) { + this.someString = someString; + } + + public String getOtherString() { + return otherString; + } + + public void setOtherString(String otherString) { + this.otherString = otherString; + } + + + public void setBoolean(boolean aBoolean) { + this.aBoolean = aBoolean; + } + + public void setStringCollection(Collection stringCollection) { + this.stringCollection = stringCollection; + } + + public void setHomeDir(Path homeDir) { + this.homeDir = homeDir; + } + + public void setSystemProperty(String systemProperty) { + this.systemProperty = systemProperty; + } + + public Collection getPathCollection() { + return pathCollection; + } + + public void setPathCollection(HashSet pathCollection) { + this.pathCollection = pathCollection; + } + + public List getIntegerList() { + return integerList; + } + + public void setIntegerList(List integerList) { + this.integerList = integerList; + } + + public ArrayList getTestConfigList() { + return testConfigList; + } + + public Collection getStringCollection() { + return stringCollection; + } + + public void setCopiedStringCollection(Iterable copiedStringCollection) { + this.copiedStringCollection = copiedStringCollection; + } + + @Validation + protected void validate() { + System.out.println("config validated"); + } +} diff --git a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigWithoutDefaultConstructor.java b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigWithoutDefaultConstructor.java index 50c0644d..b861f480 100644 --- a/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigWithoutDefaultConstructor.java +++ b/src/test/java/com/tngtech/configbuilder/testclasses/TestConfigWithoutDefaultConstructor.java @@ -5,16 +5,13 @@ import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyExtension; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyLocations; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertySuffixes; -import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; -import com.tngtech.configbuilder.annotation.valueextractor.DefaultValue; -import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; import com.tngtech.propertyloader.PropertyLoader; @PropertyExtension("testproperties") @PropertySuffixes(extraSuffixes = {"test"}) @PropertyLocations(resourcesForClasses = {PropertyLoader.class}) @PropertiesFiles("demoapp-configuration") -@LoadingOrder(value = {CommandLineValue.class, PropertyValue.class, DefaultValue.class}) +@LoadingOrder() public class TestConfigWithoutDefaultConstructor { private int number; @@ -23,7 +20,6 @@ public TestConfigWithoutDefaultConstructor(Integer i) { this.number = i; } - public int getNumber() { return number; } diff --git a/src/test/java/com/tngtech/configbuilder/testutil/LoggerExtension.java b/src/test/java/com/tngtech/configbuilder/testutil/LoggerExtension.java new file mode 100644 index 00000000..75ac4bd3 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/testutil/LoggerExtension.java @@ -0,0 +1,24 @@ +package com.tngtech.configbuilder.testutil; + +import java.io.ByteArrayOutputStream; +import org.apache.log4j.Logger; +import org.apache.log4j.SimpleLayout; +import org.apache.log4j.WriterAppender; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class LoggerExtension implements BeforeEachCallback { + + private final ByteArrayOutputStream content = new ByteArrayOutputStream(); + + @Override + public void beforeEach(ExtensionContext context) { + Logger.getRootLogger().removeAllAppenders(); + Logger.getRootLogger().addAppender(new WriterAppender(new SimpleLayout(), content)); + } + + public String getLog() { + return content.toString(); + } + +} diff --git a/src/test/java/com/tngtech/configbuilder/testutil/SystemOutExtension.java b/src/test/java/com/tngtech/configbuilder/testutil/SystemOutExtension.java new file mode 100644 index 00000000..284c5188 --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/testutil/SystemOutExtension.java @@ -0,0 +1,28 @@ +package com.tngtech.configbuilder.testutil; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +public class SystemOutExtension implements BeforeEachCallback, AfterEachCallback { + + private final ByteArrayOutputStream content = new ByteArrayOutputStream(); + private PrintStream originalOutStream; + + @Override + public void beforeEach(ExtensionContext context) { + originalOutStream = new PrintStream(System.out); + System.setOut(new PrintStream(content)); + } + + @Override + public void afterEach(ExtensionContext context) { + System.setOut(originalOutStream); + } + + public String getLog() { + return content.toString(); + } +} diff --git a/src/test/java/com/tngtech/configbuilder/util/AnnotationHelperTest.java b/src/test/java/com/tngtech/configbuilder/util/AnnotationHelperTest.java index 3f6656ac..82d00d99 100644 --- a/src/test/java/com/tngtech/configbuilder/util/AnnotationHelperTest.java +++ b/src/test/java/com/tngtech/configbuilder/util/AnnotationHelperTest.java @@ -1,25 +1,23 @@ package com.tngtech.configbuilder.util; -import com.google.common.collect.Lists; import com.tngtech.configbuilder.annotation.propertyloaderconfiguration.PropertyLoaderConfigurationAnnotation; import com.tngtech.configbuilder.annotation.typetransformer.StringCollectionToCommaSeparatedStringTransformer; import com.tngtech.configbuilder.annotation.typetransformer.TypeTransformers; import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; import com.tngtech.configbuilder.annotation.valueextractor.PropertyValue; import com.tngtech.configbuilder.annotation.valueextractor.ValueExtractorAnnotation; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.runners.MockitoJUnitRunner; - import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.Collection; import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; -import static org.junit.Assert.*; +import static org.assertj.core.api.Assertions.assertThat; -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class AnnotationHelperTest { public class TestConfig { @@ -30,42 +28,37 @@ public class TestConfig { private Collection testField; } - - private AnnotationHelper annotationHelper; + private final AnnotationHelper annotationHelper = new AnnotationHelper(); private Field field; - private Class[] annotationOrder = new Class[]{CommandLineValue.class, PropertyValue.class}; + private final Class[] annotationOrder = new Class[]{CommandLineValue.class, PropertyValue.class}; - @Before + @BeforeEach public void setUp() throws Exception { field = TestConfig.class.getDeclaredField("testField"); - annotationHelper = new AnnotationHelper(); } @Test - public void testGetAnnotationsAnnotatedWith() throws Exception { + public void testGetAnnotationsAnnotatedWith() { List result = annotationHelper.getAnnotationsAnnotatedWith(field.getDeclaredAnnotations(), ValueExtractorAnnotation.class); - assertTrue(result.contains(field.getAnnotation(CommandLineValue.class))); - assertTrue(result.contains(field.getAnnotation(PropertyValue.class))); - - assertFalse(result.contains(field.getAnnotation(TypeTransformers.class))); + assertThat(result) + .contains(field.getAnnotation(CommandLineValue.class), field.getAnnotation(PropertyValue.class)) + .doesNotContain(field.getAnnotation(TypeTransformers.class)); } - @Test - public void testGetAnnotationsInOrder() throws Exception { - List orderList = Lists.newArrayList(field.getAnnotation(CommandLineValue.class), field.getAnnotation(PropertyValue.class)); + public void testGetAnnotationsInOrder() { List result = annotationHelper.getAnnotationsInOrder(field, annotationOrder); - assertEquals(orderList, result); + assertThat(result).containsExactly(field.getAnnotation(CommandLineValue.class), field.getAnnotation(PropertyValue.class)); } @Test - public void testGetFieldsAnnotatedWith() throws Exception { - assertTrue(annotationHelper.getFieldsAnnotatedWith(TestConfig.class, PropertyValue.class).contains(field)); + public void testGetFieldsAnnotatedWith() { + assertThat(annotationHelper.getFieldsAnnotatedWith(TestConfig.class, PropertyValue.class)).contains(field); } @Test - public void testFieldHasAnnotation() throws Exception { - assertTrue(annotationHelper.fieldHasAnnotationAnnotatedWith(field, ValueExtractorAnnotation.class)); - assertFalse(annotationHelper.fieldHasAnnotationAnnotatedWith(field, PropertyLoaderConfigurationAnnotation.class)); + public void testFieldHasAnnotation() { + assertThat(annotationHelper.fieldHasAnnotationAnnotatedWith(field, ValueExtractorAnnotation.class)).isTrue(); + assertThat(annotationHelper.fieldHasAnnotationAnnotatedWith(field, PropertyLoaderConfigurationAnnotation.class)).isFalse(); } } diff --git a/src/test/java/com/tngtech/configbuilder/util/CommandLineHelperExceptionHandlingTest.java b/src/test/java/com/tngtech/configbuilder/util/CommandLineHelperExceptionHandlingTest.java new file mode 100644 index 00000000..2e36c26c --- /dev/null +++ b/src/test/java/com/tngtech/configbuilder/util/CommandLineHelperExceptionHandlingTest.java @@ -0,0 +1,70 @@ +package com.tngtech.configbuilder.util; + +import com.tngtech.configbuilder.ConfigBuilder; +import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; +import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValueDescriptor; +import com.tngtech.configbuilder.exception.ConfigBuilderException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class CommandLineHelperExceptionHandlingTest { + + private static class TestConfig { + @CommandLineValue(shortOpt = "u", longOpt = "user", required = true) + public String user; + } + + private static class TestConfigWithInvalidCommandLineValueDescriptor { + @CommandLineValue(shortOpt = "u", longOpt = "user") + public String user; + + @CommandLineValueDescriptor + private static void description() { } + } + + private static class TestConfigWithMultipleCommandLineValueDescriptors { + @CommandLineValue(shortOpt = "u", longOpt = "user") + public String user; + + @CommandLineValueDescriptor + private static String description1(String longOpt) { + return ""; + } + + @CommandLineValueDescriptor + private static String description2(String longOpt) { + return ""; + } + } + + @Test + public void testUndefinedCommandLineOption() { + String[] args = {"nd", "notDefined"}; + assertThatThrownBy(() -> ConfigBuilder.on(TestConfig.class).withCommandLineArgs(args).build()) + .isInstanceOf(ConfigBuilderException.class) + .hasMessageContaining("unable to parse command line arguments"); + } + + @Test + public void testInvalidCommandLineValueDescriptor() { + assertThatThrownBy(() -> ConfigBuilder.on(TestConfigWithInvalidCommandLineValueDescriptor.class).printCommandLineHelp()) + .isInstanceOf(ConfigBuilderException.class) + .hasMessageContaining("invalid or multiple use of the @CommandLineValueDescriptor annotation"); + + assertThatThrownBy(() -> ConfigBuilder.on(TestConfigWithInvalidCommandLineValueDescriptor.class).build()) + .isInstanceOf(ConfigBuilderException.class) + .hasMessageContaining("invalid or multiple use of the @CommandLineValueDescriptor annotation"); + } + + @Test + public void testMultipleCommandLineValueDescriptors() { + assertThatThrownBy(() -> ConfigBuilder.on(TestConfigWithMultipleCommandLineValueDescriptors.class).printCommandLineHelp()) + .isInstanceOf(ConfigBuilderException.class) + .hasMessageContaining("invalid or multiple use of the @CommandLineValueDescriptor annotation"); + + assertThatThrownBy(() -> ConfigBuilder.on(TestConfigWithMultipleCommandLineValueDescriptors.class).build()) + .isInstanceOf(ConfigBuilderException.class) + .hasMessageContaining("invalid or multiple use of the @CommandLineValueDescriptor annotation"); + } +} diff --git a/src/test/java/com/tngtech/configbuilder/util/CommandLineHelperTest.java b/src/test/java/com/tngtech/configbuilder/util/CommandLineHelperTest.java index bfa0cb32..8dd5fcd8 100644 --- a/src/test/java/com/tngtech/configbuilder/util/CommandLineHelperTest.java +++ b/src/test/java/com/tngtech/configbuilder/util/CommandLineHelperTest.java @@ -1,94 +1,99 @@ package com.tngtech.configbuilder.util; -import com.google.common.collect.Sets; import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValue; +import com.tngtech.configbuilder.annotation.valueextractor.CommandLineValueDescriptor; import com.tngtech.configbuilder.configuration.ErrorMessageSetup; -import com.tngtech.configbuilder.exception.ConfigBuilderException; +import java.util.ArrayList; +import java.util.List; import org.apache.commons.cli.CommandLine; -import org.apache.commons.cli.GnuParser; +import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; -import java.lang.reflect.Field; -import java.util.Set; +import static java.util.Comparator.comparing; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.*; - -@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) public class CommandLineHelperTest { private static class TestConfig { - @CommandLineValue(shortOpt = "u", longOpt = "user", required = true) + @CommandLineValue(shortOpt = "u", longOpt = "user", required = true, description = "some static description string") public String aString; - @CommandLineValue(shortOpt = "v", longOpt = "vir", required = false) + @CommandLineValue(shortOpt = "v", longOpt = "vir") public String anotherString; + + @CommandLineValueDescriptor + private static String description(String longOpt) { + if ("vir".equals(longOpt)) { + return "some dynamically generated description"; + } + return ""; + } } private CommandLineHelper commandLineHelper; - private String[] args = null; + private final String[] args = null; @Mock private Options options; @Mock - private GnuParser parser; + private DefaultParser parser; @Mock private CommandLine commandLine; @Mock private ConfigBuilderFactory configBuilderFactory; @Mock - private AnnotationHelper annotationHelper; - @Mock private ErrorMessageSetup errorMessageSetup; - - @Before + @BeforeEach public void setUp() throws Exception { - - when(configBuilderFactory.getInstance(AnnotationHelper.class)).thenReturn(annotationHelper); + when(configBuilderFactory.getInstance(AnnotationHelper.class)).thenReturn(new AnnotationHelper()); when(configBuilderFactory.getInstance(ErrorMessageSetup.class)).thenReturn(errorMessageSetup); commandLineHelper = new CommandLineHelper(configBuilderFactory); - - Set fields = Sets.newHashSet(TestConfig.class.getDeclaredFields()); - when(annotationHelper.getFieldsAnnotatedWith(TestConfig.class, CommandLineValue.class)).thenReturn(fields); - when(parser.parse(options, args)).thenReturn(commandLine); } @Test public void testGetCommandLine() throws Exception { - when(configBuilderFactory.createInstance(GnuParser.class)).thenReturn(parser); + when(parser.parse(options, args)).thenReturn(commandLine); + when(configBuilderFactory.createInstance(DefaultParser.class)).thenReturn(parser); when(configBuilderFactory.createInstance(Options.class)).thenReturn(options); ArgumentCaptor